C++ 为什么可以';当涉及虚拟继承时,静态强制转换是否可用于向下强制转换?

C++ 为什么可以';当涉及虚拟继承时,静态强制转换是否可用于向下强制转换?,c++,virtual-inheritance,downcast,static-cast,C++,Virtual Inheritance,Downcast,Static Cast,考虑以下代码: struct Base {}; struct Derived : public virtual Base {}; void f() { Base* b = new Derived; Derived* d = static_cast<Derived*>(b); } struct Base{}; 派生结构:公共虚拟基{}; void f() { 基*b=新导出的; 导出*d=静态_转换(b); } 这是标准([n3290:5.2.9/2])所禁止的,

考虑以下代码:

struct Base {};
struct Derived : public virtual Base {};

void f()
{
    Base* b = new Derived;
    Derived* d = static_cast<Derived*>(b);
}
struct Base{};
派生结构:公共虚拟基{};
void f()
{
基*b=新导出的;
导出*d=静态_转换(b);
}
这是标准(
[n3290:5.2.9/2]
)所禁止的,因此代码不会编译,因为
派生的
实际上继承自
基本的
。从继承中删除
虚拟
将使代码有效


此规则存在的技术原因是什么?

static\u cast
是一种编译时构造。它在编译时检查强制转换的有效性,如果强制转换无效,则给出编译错误

virtual
ism是一种运行时现象

两者不能同时进行

C++03标准§5.2.9/2和§5.2.9/9 ar与本案例相关

“指向cv1 B的指针”类型的右值(其中B为类类型)可转换为“指向cv2 D的指针”类型的右值,其中D为从B派生的类(第10条),如果存在从“指向D的指针”到“指向B的指针”的有效标准转换(4.10),则cv2与cv1具有相同的cv资格,或大于cv1的cv资格,而B不是D的虚拟基类。空指针值(4.10)转换为目标类型的空指针值。如果类型为“指向cv1 B的指针”的右值指向实际上是类型为D的对象的子对象的B,则生成的指针指向类型为D的封闭对象。否则,强制转换的结果未定义


我想,这是由于具有虚拟继承的类具有不同的内存布局。父对象必须在子对象之间共享,因此只能连续放置其中一个。这意味着,您不能保证能够分离连续的内存区域以将其视为派生对象。

技术问题是,无法从
Base*
计算出
Base
子对象的开始与
派生的
对象的开始之间的偏移量

在您的示例中,它看起来不错,因为只有一个类具有
Base
Base,因此继承是虚拟的似乎无关紧要。但是编译器不知道是否有人定义了另一个
类Derived2:public virtual Base,public-Derived{}
,并且正在抛出一个指向该类的
Base
子对象的
Base*
。通常[*],在
Derived2
基本
子对象和
派生
子对象之间的偏移量可能与
基本
子对象和最派生类型为
派生
的对象的完整
派生
对象之间的偏移量不同,正是因为
Base
实际上是继承的

因此,无法知道完整对象的动态类型,以及给定强制转换的指针与所需结果之间的不同偏移量,具体取决于该动态类型。因此,演员阵容是不可能的

您的
Base
没有虚拟函数,因此没有RTTI,因此肯定无法确定完整对象的类型。即使
Base
确实有RTTI(我不知道为什么),但我想在不检查
dynamic_cast
的情况下,仍然禁止该cast


[*]我的意思是,如果这个例子不能证明这一点,那么继续添加更多的虚拟继承,直到找到偏移量不同的情况;-)

考虑以下函数
foo

#include <iostream>

struct A
{
    int Ax;
};

struct B : virtual A
{
    int Bx;
};

struct C : B, virtual A
{
    int Cx;
};


void foo( const B& b )
{
    const B* pb = &b;
    const A* pa = &b;

    std::cout << (void*)pb << ", " << (void*)pa << "\n";

    const char* ca = reinterpret_cast<const char*>(pa);
    const char* cb = reinterpret_cast<const char*>(pb);

    std::cout << "diff " << (cb-ca) << "\n";
}

int main(int argc, const char *argv[])
{
    C c;
    foo(c);

    B b;
    foo(b);
}

从根本上说,没有真正的理由,但目的是
static\u cast
非常便宜,最多只需要添加或添加一个 将常数减去指针。而且没有办法 以低廉的成本实施您想要的演员阵容;基本上,因为 对象内
导出的
基础
的相对位置可能会改变 如果有额外的继承,转换将需要良好的 处理
动态广播的开销
;委员会成员 可能认为这会破坏使用
static\u cast

而不是
dynamic\u cast

static\u cast
只能执行那些在编译时已知类之间内存布局的转换
dynamic_cast
可以在运行时检查信息,这允许更准确地检查强制转换的正确性,以及读取有关内存布局的运行时信息

虚拟继承将运行时信息放入每个对象中,该信息指定
派生
之间的内存布局。是一个接一个,还是有额外的差距?由于
static\u cast
无法访问此类信息,编译器将采取保守的操作,只给出一个编译器错误


更详细的信息:

考虑一个复杂的继承结构,其中-由于多重继承-存在
Base
的多个副本。最典型的场景是菱形继承:

class Base {...};
class Left : public Base {...};
class Right : public Base {...};
class Bottom : public Left, public Right {...};
在此场景中,
底部
左侧
右侧
组成,其中每一个都有自己的
基础
副本。以上所有类的内存结构在编译时都是已知的,可以毫无问题地使用
static\u cast

我们现在考虑类似的结构,但虚拟继承的<代码>基础< /代码>:

class Base {...};
class Left : public virtual Base {...};
class Right : public virtual Base {...};
class Bottom : public Left, public Right {...};
使用虚拟继承可以确保在创建
Bottom
时,它只包含
Base
的一个副本,该副本在对象部分
Left
Right
之间共享。
Bottom
对象的布局可以是:

Base part
Left part
Right part
Bottom part

现在,考虑将代码< >底部<代码> > <代码>右<代码>

Base part
Left part
Right part
Bottom part