是否存在将实际Base向下转换为Derived的情况?

Ad *_*d N 9 c++ casting undefined-behavior

在一般情况下,从(动态)下降Base到其中一个派生类是(非常应得的)未定义行为Derived

显而易见的UB

class Base
{
public:
    virtual void foo()
    { /* does something */ }

    int a;
}

class Derived : public Base
{
public:
    virtual void foo()
    { /* does something different */ }

    double b;
}

Base obj;
Derived derObj = *static_cast<Derived *>(&obj);  // <- here come the demons
Run Code Online (Sandbox Code Playgroud)

在目前的编译器实现方法中,这里显然至少存在Vtable和b中包含垃圾值的值不一致的问题.因此,在这些条件下,标准没有定义向下转换的行为是有道理的.

不那么明显的天真案例

然而,我很想知道在特定情况下这条规则是否有一些让步? 举个例子:

class Base
{
public:
    void foo()
    { /* does something */ }

    int a = 1;
    double b = 2.;
}

class DerivedForInt : public Base
{
    int getVal()
    { return a }
}

Base obj;
DerivedForInt derObj = *static_cast<DerivedForInt *>(&obj);  // <- still an UB ?
Run Code Online (Sandbox Code Playgroud)

在这里,我们可以很容易地想象编译器做正确的事 但从标准的角度来看,它仍未定义吗?

编辑:static_cast是一个随机选择,用于插图目的,如果与其他演员一起工作也很有趣!

use*_*290 7

好的,我可能会因为这个答案而被切成碎片......

显然,正如其他答案所述,这是未定义的行为,如标准中所见.但是,如果您的Base类具有标准布局,并且您的DerivedForInt类不添加新数据成员,则它将具有相同(标准)布局.

在这些情况下,即使是UB,你的阵容也不应该造成任何麻烦.根据其中一位消息来源,做一个至少是安全的

DerivedForInt *derived = reinterpret_cast<DerivedForInt*>(&base.a);
Run Code Online (Sandbox Code Playgroud)

资料来源:

什么是聚合和POD以及它们如何/为何特殊?

P ++中的POD和继承11.结构的地址=第一个成员的地址吗?

从第二个链接:

这是定义,来自标准第9节[类]:

标准布局类是一个类:

  • 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
  • 没有虚函数(10.3),也没有虚基类(10.1),
  • 对所有非静态数据成员具有相同的访问控制(第11条),
  • 没有非标准布局基类,
  • 或者在最派生类中没有非静态数据成员,并且最多只有一个具有非静态数据成员的基类,或者没有具有非静态数据成员的基类,并且
  • 没有与第一个非静态数据成员相同类型的基类.

然后保证你想要的财产(第9.2节[class.mem]):

指向标准布局结构对象的指针,使用reinterpret_cast进行适当转换,指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然.

这实际上比旧的要求更好,因为通过添加非平凡的构造函数和/或析构函数不会丢失reinterpret_cast的能力.