我在刚才问过这个上comp.std.c++,但没有得到答复。
我只想引用我在那里的帖子,只需稍作修改。
标准布局类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是错误的,标准不应该捕获这个。
语言回到N2172暗示多重继承可能会造成麻烦,需要在标准布局类中被禁止以确保ABI兼容性;然而,这最终是被允许的,在这种情况下,需求是没有意义的。
供参考,1.8/5-6:
5除非是位字段(9.6),否则大多数派生对象应具有非零大小,并占用一个或多个字节的存储空间。基类子对象的大小可能为零。小型可复制或标准布局类型(3.9)的对象应占用连续的存储字节. 6除非对象是位字段或零大小的基类子对象,否则该对象的地址是它所占用的第一个字节的地址。两个不同的对象,它们既不是位字段,也不是零大小的基类子对象,应该有不同的地址。 (脚注)在“as- if”规则下,如果程序不能观察到差异,则允许实现将两个对象存储在同一个计算机地址上,或者根本不存储对象。
补充说明:
10.1/8提到了同样的神秘内容5.10,但它也只是一个信息说明。
注:…基类子对象可以为零大小(第9条);但是,不能在相同的地址(5.10)上分配两个具有相同类类型且属于相同的大多数派生对象的子对象。-尾注
GCC似乎保证了相同类型的空基子对象具有唯一的地址。示例程序和输出。 --这似乎足以保证给定类型的对象通过地址进行唯一标识。这将超出C++对象模型的保证,第1.8节。当然,这是个好主意,但这似乎不是标准所要求的。类似地,平台ABI可以将此保证扩展到第一个成员别名为空基的类。该语言为ABI设定了最低要求;ABI可以添加语言特性,其他ABI也可以这样做,标准的追赶过程很容易出错。
我的问题是,给定的需求是否在标准的上下文中完成了什么,而不是它是否与其他ABI保证一起对用户有用。有证据表明,这种独特的地址担保是有意的,而且只是偶然遗漏,也会使这一要求更有意义。
总结一下答案(或者说我的结论):
从理论上讲,这一要求并不能确保任何事情,因为无论如何都有可能确保给定类型的所有对象都有不同的地址。当空基类子对象的地址与另一个对象(另一个基或成员)发生冲突时,编译器可以简单地在结构中为它分配一个任意位置。由于标准布局规则只描述数据成员的位置(可能是继承的),空基的位置仍未指定,类似的标准布局类之间可能不兼容。(据我所知,非空基地的位置仍未明确,在这种情况下“第一成员”是什么意思,但在任何情况下都必须是一致的。)
实际上,该需求允许实现继续使用现有的ABI,只要它们包括空基类优化。当违反要求时,现有编译器可以禁用EBO,以避免与第一成员的地址重合的基地址。如果标准不以这种方式限制程序,那么库和程序就必须用更新的C++0x编译器…重新编译不值得!
发布于 2010-10-10 23:01:12
两个带有公共基类的空基类必须在同一个地址生成基类的两个实例。
我不这样认为。事实上,快速检查我的g++副本表明我有两个不同的A对象地址。也就是说,上面的代码不是真的。
事实上,根据编写类的方式,我们必须有两个A对象。如果两个对象共享相同的地址,那么它们在任何意义上都不是两个不同的对象。因此,需要为A对象的实例存在不同的地址。
假设A的定义如下:
class A
{
static std::set<A*> instances;
A() { instances.insert(this); }
~A() { instances.remove(this); }
}如果允许A的两个副本共享一个地址,则此代码将无法正常工作。我相信,在这种情况下,我们应该对A的不同副本有明确的地址,当然,正是这种情况使我避免了多重继承。
https://stackoverflow.com/questions/3902513
复制相似问题