C ++不能通过虚拟基础A从基础A转换到派生typesB.

我有三个class级:

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

试图从A *到B *静态转换我得到以下错误:

 cannot convert from base A to derived type B via virtual base A 

为了理解投射系统,你需要在对象模型中潜水。

一个简单的层次结构模型的经典表示是遏制:如果BA派生,那么B对象实际上将包含一个A子对象以及它自己的属性。

使用这个模型,向下转换是一个简单的指针操作,由编译时已知的偏移量决定,取决于B的存储器布局。

这就是static_cast所做的:静态转换被称为静态转换,因为转换所需的计算是在编译时进行的,无论是指针运算还是转换(*)。

但是,当virtualinheritance踢进事情往往变得更加困难。 主要问题是,使用virtualinheritance,所有子类共享子对象的相同实例。 为了做到这一点, B将有一个指针,而不是一个适当的, A基类对象将被实例化在B之外。

因此,编译时不可能推导出必要的指针algorithm:它取决于对象的运行时types。

无论何时存在运行时types依赖关系,都需要RTTI(运行时types信息),并且使用RTTI进行types转换是dynamic_cast的工作。

综上所述:

  • 编译时downcast: static_cast
  • 运行时downcast: dynamic_cast

另外两个也是编译时间,但它们非常具体,以至于很容易记住它们是什么…而且它们是臭的,所以最好不要使用它们。

(*) 正如@curiousguy在评论中指出的那样,这只适用于向下转换。 static_cast允许上传,而不pipe虚拟或简单的inheritance,虽然然后转换也是不必要的。

据我所知,你需要使用dynamic_cast因为inheritance是virtual ,你是向下转换的。

在这种情况下不能使用static_cast ,因为在编译时编译器不知道B相对于A的偏移量。 偏移量必须根据最大派生对象的确切types在运行时进行计算。 所以你必须使用dynamic_cast

是的,你必须使用dynamic_cast,但是你必须使基类A是多态的,例如通过添加一个虚拟的dtor。

根据标准文件,

5.2.9 – 9节, 静态铸造

types“指向cv1 B的指针”的右值可以被转换为types“指向cv2 D的指针”的右值,其中D是从B派生的类(子句10),如果有效的标准(4.10)存在从“指向D的指针”到“指向B的指针”的转换,cv2与cv1具有相同的cv限定或者更高的cv限定,并且B既不是D的虚拟基类也不是基类D的虚拟基类

因此,这是不可能的,你应该使用dynamic_cast

$ 5.2.9 / 2-“如果声明”T t(e);“是格式良好的,则对于某些发明的临时variablest,可以使用forms为static_cast(e)的static_cast将expression式e明确地转换为typesT (8.5)“。

在你的代码中,你正在尝试static_cast'T = B *'和'e = A *'

现在'B * t(A *)'在C ++中是不完整的,但'A * t(B *)'是因为'A'是一个虚拟的, 。

我不知道这是否“安全”,但。

假设

B从A(和A纯虚)

因为我知道指向B的指针仍然是指向B的指针。

  class A { virtual void doSomething(const void* p) const =0; }; class B { public: int value; virtual void doSomething(const void*p)const { const B * other = reinterpret_cast<const B*>(p); cout<<"hello!"<< other->value <<endl; } }; int main() { B foo(1),bar(2); A * p = &foo, q=&bar; p->doSomething(q); return 0; } 

这个程序执行并正确地返回打印“你好!” 和另一个对象的值(在这种情况下是“2”)。

顺便说一句,我正在做的事情是非常不安全的(我个人给每个类都给了一个不同的ID,而且我在重新解释铸造之后声明当前的ID等于其他ID,以确保我们正在做两个相等的类)你看我把自己限制在“const”方法中。 因此,这将与“非常量”方法一起工作,但是如果你做错了什么,捕捉这个bug几乎是不可能的。 即使有断言,即使应该失败,assertion也有1个机会(断言(ID == other-> ID);)

顺便说一句,一个好的OOdevise不应该要求这样的东西,但在我的情况下,我试图重构/重新devise代码,而不能放弃重新解释铸造的使用。 一般来说你可以避免这种事情。