【发布时间】:2010-10-10 22:39:41
【问题描述】:
我在 comp.std.c++ 上asked this a while ago 并没有得到回复。
我只是在稍作修改的情况下引用我的帖子。
标准布局类的最后一个要求,9/6,是必要的还是有用的?
提供脚注说明:
这确保了两个子对象 具有相同的类类型,并且 属于同一个最衍生对象 不在同一个地址分配 (5.10)。
单独来看,脚注是不正确的。两个空基类 公共基类可能会产生两个基类实例 同一个地址。
struct A {};
struct B : A {};
struct C : A {};
struct D : B, C {};
D d;
static_cast<A*>(static_cast<B*>(&d))
== static_cast<A*>(static_cast<C*>(&d)); // allowed per 1.8/5
在 5.10 的上下文中,子对象仅在 成员指针的比较要求。基础子对象是 无关的。此外,它不会使 给一个(标量)指针到一个 成员子对象和指向高于的基础子对象的指针 比较指向基本子对象的指针。
C++03 中没有这样的限制。即使有一个 ABI 要求每个成员都 分配在与任何相同类型的基址不同的地址,但 已经允许对上面的代码进行空基类优化,我 认为 ABI 有问题,标准不应该捕捉到这一点。
语言goes back to N2172 这表明多重继承可能会引起麻烦和需要 在标准布局类中不允许使用以确保ABI compatibility; 但是,这最终是允许的,因此要求 没有意义。
供参考,1.8/5-6:
5 除非它是位域 (9.6),否则 大多数派生对象应具有 非零大小,应占用一个或 更多字节的存储空间。基类 子对象的大小可能为零。一个 可简单复制的对象或 标准布局类型 (3.9) 应 占用连续字节的存储空间。
6 除非对象是位域或 零大小的基类子对象, 该对象的地址是地址 它占用的第一个字节。二 两者都不是的不同对象 位域或基类子对象 大小为零的应具有不同的 地址。
(脚注)在“as-if”规则下,如果程序无法观察到差异,则允许实现将两个对象存储在同一机器地址或根本不存储对象。
补充说明:
10.1/8 指的是与 5.10 相同的神秘内容,但它也只是一个信息说明。
[注意:……基类子对象的大小可能为零(第 9 条);但是,两个具有相同类类型且属于同一个最派生对象的子对象不能分配在同一地址(5.10)。 ——尾注]
GCC 似乎保证相同类型的空基本子对象被赋予唯一地址。 Example program and output. 这似乎足以保证给定类型的对象由地址唯一标识。这将超出 C++ 对象模型§1.8 的保证范围。当然这是个好主意,但标准似乎没有要求。同样,平台 ABI 可以将此保证扩展到第一个成员别名为空基的类。该语言设定了 ABI 的最低要求;一个 ABI 可以添加语言特性,其他 ABI 也可以效仿,标准的追赶过程很容易出错。
我的问题是,给定的要求是否在标准的上下文中完成了任何事情,而不是与其他 ABI 保证一起对用户是否有用。证明这种唯一地址保证是有意的并且只是偶然省略的证据,也会使要求更有意义。
总结答案(或者我的结论,无论如何):
该要求理论上并不能确保任何事情,因为无论如何都可以确保给定类型的所有对象具有不同的地址。当空基类子对象的地址与另一个对象(另一个基类或成员)冲突时,编译器可以简单地为其分配结构内的任意位置。由于标准布局规则仅描述数据成员的位置(可能继承),空基的位置仍未指定,并且可能在类似的标准布局类之间不兼容。 (据我所知,非空碱基的位置仍未指定,然后不清楚在这种情况下“第一个成员”是什么意思,但无论如何它们必须保持一致。)
实际上,只要包含空基类优化,该要求就允许实现继续使用现有 ABI。现有编译器可能会在违反要求时禁用 EBO,以避免基地址与第一个成员的地址重合。如果标准没有以这种方式限制程序,库和程序将不得不使用更新的 C++0x 编译器重新编译……不值得!
【问题讨论】: