关于强制转换:C ++无法通过虚拟基础A从基础A转换为派生类型B.

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

我有三门课:

1
2
3
4
5
6
class A {};

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

class D: public B, public C {};

尝试从A*到B*的静态强制转换时,我得到以下错误:

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

为了理解投射系统,您需要深入到对象模型中。

简单层次模型的经典表示是包容:如果B派生自A,那么B对象实际上将包含一个A子对象及其自身属性。

在这个模型中,向下转换是一个简单的指针操作,通过编译时已知的偏移量进行,偏移量取决于EDOCX1的内存布局(5)。

静态强制转换就是这样做的:静态强制转换被称为静态强制转换,因为强制转换所需的计算是在编译时完成的,无论是指针算术还是转换(*)。

然而,当virtual继承开始时,事情往往变得更加困难。主要问题是,对于virtual继承,所有子类都共享子对象的同一个实例。为了做到这一点,B将有一个指向A的指针,而不是一个A适当的指针,并且A基类对象将在B之外实例化。

因此,在编译时不可能推导出必要的指针算法:它取决于对象的运行时类型。

每当存在运行时类型依赖关系时,您都需要RTTI(运行时类型信息),使用RTTI进行强制转换是动态强制转换的工作。

综上所述:

  • 编译时下降:static_cast
  • 运行时间下降:dynamic_cast

另外两个也是编译时的类型转换,但是它们非常具体,所以很容易记住它们的用途…而且它们很臭,所以无论如何最好不要使用它们。

(*)正如@curiousguy在评论中所指出的,这只适用于下推。static_cast允许上溯,而不考虑虚拟继承或简单继承,尽管这样的转换也是不必要的。


据我所知,你需要使用dynamic_cast,因为继承权是virtual,而你在贬低。


在这种情况下,不能使用static_cast,因为编译器在编译时不知道b相对于a的偏移量。运行时必须根据最派生对象的确切类型计算偏移量。因此,您必须使用dynamic_cast


根据标准文件,

第5.2.9-9节,静态铸造,

An rvalue of type"pointer to cv1 B," where B is a class type, can be converted to an rvalue of type"pointer to cv2
D," where D is a class derived (clause 10) from B, if a valid standard conversion from"pointer to D" to"pointer to B"
exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class
of D nor a base class of a virtual base class of D.

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


是的,您必须使用动态类型转换,但是您必须使基类成为多态的,例如通过添加虚拟DTOR。


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

假设

B源于A(和纯虚拟)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    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几乎是不可行的。即使有断言,40亿人中有1人有机会成功断言,即使它应该失败。(断言(id==other->id);)

顺便说一句。。一个好的OO设计不应该需要这样的东西,但是在我的例子中,我试图重构/重新设计代码,而不能放弃对reinterpret casting的使用。一般来说,你可以避免这种事情。


$5.2.9/2-"An expression e can be
explicitly converted to a type T using
a static_cast of the form
static_cast(e) if the declaration
"T t(e);" is well-formed, for some
invented temporary variable t (8.5)."

在您的代码中,您正尝试使用"t=b*"和"e=a*"进行静态u强制转换。

现在'b*t(a*)在C++中不是很好地形成(但是‘a*t(b*)’是因为‘a’是一个虚拟的明确的和可访问的‘b’基。因此,代码给出了错误。