Meh*_*dad 3 c++ casting undefined-behavior
乍一看,这看起来像未定义的行为......
#include <iostream>
struct SomeBaseClass
{
// ...
};
struct MyFakerClass
{
SomeBaseClass base;
void foo() { std::cout << "hello" << std::endl; }
};
int main()
{
MyFakerClass c;
MyFakerClass *p = static_cast<MyFakerClass *>(static_cast<void *>(&c.base));
p->foo();
}
Run Code Online (Sandbox Code Playgroud)
...但是如果保证对象的第一个字段与对象具有相同的地址,那么这一定是安全的,对吧?还是有其他一些干预的规则吗?
指向标准布局结构对象的指针,使用reinterpret_cast进行适当转换,指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然.[注意:因此,在标准布局结构对象中可能存在未命名的填充,但不是在其开头,以实现适当的对齐. - 结束说明]
(C++ 11,§9.2,21)
所以,只要一个类是"标准布局",这是安全的,即:
标准布局类是一个类:
- 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
- 没有虚函数(10.3),也没有虚基类(10.1),
- 对所有非静态数据成员具有相同的访问控制(第11条),
- 没有非标准布局基类,
- 或者在最派生类中没有非静态数据成员,并且最多只有一个具有非静态数据成员的基类,或者没有具有非静态数据成员的基类,并且
- 没有与第一个非静态数据成员相同类型的基类.
(C++ 11,§9,7)
关于虚拟内容的所有要求都是因为vptr:如上所述,许多编译器将它作为第一个隐藏成员,但允许它们放在任何地方,或者,如果它们能够,则根本省略它或实现虚拟以其他方式调度,因此虚拟通常以未指定的方式改变类的布局.
"相同的访问控制条款"是因为
未指定具有不同访问控制的非静态数据成员的分配顺序.
(C++ 11,§9.2,15)
我想这可能是允许编译器通过访问控制重新排序/分组类的成员(即在编译器内部,类的IR可能包含每个访问控制说明符下的成员列表,这不允许保持原始排序 - 所有交错的公共/私有/受保护部分将被分组,例如私有成员可以默认放在类前面).
没有非标准布局成员/基类"递归"子句是为了确保该类是标准布局作为一个整体(基类和成员是它的一部分).
有关其他内容可能影响类布局的其他详细信息,请参见§9.2.
请注意,这是一个比POD类弱的条件(正如我在上面的注释中错误地说的那样),因为如果一个类是POD,那么它是标准布局和平凡的(§910)