在钻石层次结构中向下倾斜

log*_*og0 23 c++ casting virtual-inheritance static-cast diamond-problem

为什么static_cast不能从虚拟基地垂头丧气?

struct A {};
struct B : public virtual A {};
struct C : public virtual A {};
struct D : public B, public C {};

int main()
{
  D d;
  A& a = d;
  D* p = static_cast<D*>(&a); //error
}  
Run Code Online (Sandbox Code Playgroud)

g ++ 4.5说:

 error: cannot convert from base ‘A’ to derived type ‘D’ via virtual base ‘A’
Run Code Online (Sandbox Code Playgroud)

解决方案是使用dynamic_cast?但为什么.什么是理性?

- 编辑 -
下面非常好的答案.没有答案详细说明子对象和vtable最终如何订购.以下文章为gcc提供了一些很好的例子:http:
//www.phpcompiler.org/articles/virtualinheritance.html#Downcasting

Jam*_*nze 11

显而易见的答案是:因为标准是这样说的.标准中背后的动机是static_cast 应该接近于微不足道的 - 最多只是向指针添加或减去常量.对于虚拟基础的向下转换需要更复杂的代码:甚至可能在某个地方的vtable中有一个额外的条目.(它需要的不仅仅是常量,因为如果有进一步的推导,D相对的位置 A可能会改变.)转换显然是可行的,因为当你在一个虚拟函数上调用A*,并且函数被实现时D,编译器必须这样做它,但额外的开销被认为是不合适的static_cast.(据推测,static_cast在这种情况下使用的唯一原因是优化,因为dynamic_cast通常是首选的解决方案.因此static_cast,无论何时可能都是昂贵 dynamic_cast的,为什么要支持它.)

  • 你错了.其他回答者已经指出,"A"和"D"的相对位置取决于总继承层次结构.对于任何给定的大多数派生类,它是一个常量,但是给定一个"A*",编译器在不访问动态信息的情况下无法知道派生类最多的类. (2认同)
  • +1这个*"标准背后的动机是static_cast应该接近于微不足道 - 最多是对指针的一个常量的简单加法或减法.其中,向虚拟基础的向下转换将需要更复杂的代码"* (2认同)

Jan*_*dec 10

因为如果对象实际上是类型E(从D派生),则A子对象相对于D子对象的位置可能与对象实际不同D.

如果你考虑从A转换为C,它实际上已经发生了.当你分配C时,它必须包含A的实例并且它存在于某个特定的偏移处.但是当你分配D时,C子对象引用了B附带的A实例,所以它的偏移是不同的.

  • @Space_COwbOy,@ Johnson:问题在于虚拟继承意味着会有一个"A"的子对象.现在,如果你将`struct E:virtual A,UnrelatedType,D {}`添加到层次结构中,你可以获得一个指向`E`对象的`D`指针,但是`A`从该指针的偏移量将会有所不同,具体取决于在尖头物体的类型上. (5认同)