将对象的第一个字段强制转换为对象的类型是否真的安全?

Is casting the first field of an object to the object's type actually safe?

本文关键字:对象 是否 类型 真的 安全 第一个 字段 转换      更新时间:2023-10-16

乍一看,这看起来像是未定义的行为。。。

#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();
}

但是,如果一个对象的第一个字段保证与该对象具有相同的地址,那么这一定是安全的,对吧?还是有其他规则介入?

指向标准布局结构对象的指针,使用interpret_cast进行适当转换,指向其初始成员(或者,如果该成员是位字段,则指向其所在的单元),反之亦然[注意:因此,标准布局结构对象中可能有未命名的填充,但不是在其开头,这是实现适当对齐所必需的。--结束注释]

(C++11,§9.2,¶21)

因此,只要一个类是"标准布局",即:,这是安全的

标准布局类是指:

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

(C++11,§9,¶7)

关于虚拟内容的所有要求都是因为vptr:如上所述,许多编译器将其作为第一个隐藏成员,但他们可以将其放在任何地方,或者如果可以的话,可以完全省略它,或者以其他方式实现虚拟调度,因此virtual通常会以未指定的方式更改类的布局。

"相同的访问控制条款"是因为

具有不同访问控制的非静态数据成员的分配顺序未指定。

(C++11,§9.2,¶15)

我想这可能是为了允许编译器通过访问控制对类的成员进行重新排序/分组(即,在编译器内部,类的IR可能包含每个访问控制说明符下的成员列表,这不允许保留原始顺序-所有交错的公共/私有/受保护部分都将被分组,例如,默认情况下,私有成员可能会放在类的前面)。

无非标准布局成员/基类的"递归"子句是为了确保类作为一个整体是标准布局(基类和成员是其中的一部分)。

关于为什么这些东西的其余部分会影响类的布局的更多细节可以在§9.2中找到。


注意,这是一个比类是POD弱的条件(正如我在上面的评论中错误地说的),因为如果一个类既是标准布局又是琐碎的(§9¶10),那么它就是POD