【发布时间】:2011-11-03 10:11:27
【问题描述】:
我需要一种方法来验证在编译期间指向另一个类(派生或基类)的指针的向上/向下转换不会更改指针值。也就是说,演员表等价于reinterpret_cast。
具体来说,场景如下:我有一个Base 类和一个Derived 类(显然是从Base 派生的)。还有一个模板Wrapper 类,它包含一个指向作为模板参数指定的类的指针。
class Base
{
// ...
};
class Derived
:public Base
{
// ...
};
template <class T>
class Wrapper
{
T* m_pObj;
// ...
};
在某些情况下,我有一个Wrapper<Derived> 类型的变量,我想调用一个接收(const)引用ro Wrapper<Base> 的函数。显然这里没有自动转换,Wrapper<Derived> 不是从Wrapper<Base> 派生的。
void SomeFunc(const Wrapper<Base>&);
Wrapper<Derived> myWrapper;
// ...
SomeFunc(myWrapper); // compilation error here
有一些方法可以在标准 C++ 的范围内处理这种情况。比如这样:
Derived* pDerived = myWrapper.Detach();
Wrapper<Base> myBaseWrapper;
myBaseWrapper.Attach(pDerived);
SomeFunc(myBaseWrapper);
myBaseWrapper.Detach();
myWrapper.Attach(pDerived);
但我不喜欢这样。这不仅需要笨拙的语法,而且还会产生额外的代码,因为Wrapper 有一个不平凡的 d'tor(您可能已经猜到了),而且我正在使用异常处理。 OTOH,如果指向Base 和Derived 的指针相同(就像在这个例子中,因为没有多重继承) - 可以将myWrapper 转换为所需的类型并调用SomeFunc,它会起作用!
因此我将以下内容添加到Wrapper:
template <class T>
class Wrapper
{
T* m_pObj;
// ...
typedef T WrappedType;
template <class TT>
TT& DownCast()
{
const TT::WrappedType* p = m_pObj; // Ensures GuardType indeed inherits from TT::WrappedType
// The following will crash/fail if the cast between the types is not equivalent to reinterpret_cast
ASSERT(PBYTE((WrappedType*)(1)) == PBYTE((TT::WrappedType*)(WrappedType*)(1)));
return (TT&) *this; // brute-force case
}
template <class TT> operator const Wrapper<TT>& () const
{
return DownCast<Wrapper<TT> >();
}
};
Wrapper<Derived> myWrapper;
// ...
// Now the following compiles and works:
SomeFunc(myWrapper);
问题在于,在某些情况下,蛮力强制转换无效。例如在这种情况下:
class Base
{
// ...
};
class Derived
:public AnotherBase
,public Base
{
// ...
};
这里指向Base 的指针的值与Derived 不同。因此Wrapper<Derived> 不等于Wrapper<Base>。
我想检测并阻止这种无效的垂头丧气的尝试。我已经添加了验证(如您所见),但它可以在 run-time 中使用。也就是说,代码会编译并运行,并且在运行时会在调试构建中出现崩溃(或断言失败)。
这很好,但我想在编译时捕捉到它并让构建失败。一种 STATIC_ASSERT。
有没有办法做到这一点?
【问题讨论】:
-
也许在这里使用
static_castreturn (TT&) *this; // brute-force case而不是 c-cast 可能会有所帮助,c-cast 可能正在执行 reinterpret_cast ,这确实会破坏一切。如果两个类共享继承,编译器应该使用static_cast使指针指向正确的位置。 -
@RedX:在这个特定的地方,这相当于
static_cast,因为rwo 类Wrapper<Base>和Wrapped<Derived>不相关
标签: c++