如果类包含基类类型的成员作为第一个元素,然后是其他成员,编译器可以优化空基础吗?

时间:2017-07-04 22:23:54

标签: c++ language-lawyer

考虑:

struct empty {};
struct child : empty {
    empty a[sizeof(int) / sizeof(empty)];
    int b;
};
// Assume sizeof(int) >= sizeof(empty)

标准是否要求sizeof(child)超过2 * sizeof(int)

我看到它的方式,没有什么禁止baseclass-subobject与成员b共享其地址。

如果没有,是否有任何共同的ABI利用它?

2 个答案:

答案 0 :(得分:4)

让我们首先检查允许空基类优化的规则,使其熟悉术语:

  

N4659标准草案[intro.object]

     

7除非它是一个位域(12.2.4),否则一个派生最多的对象应具有非零大小并占用一个或多个   存储字节。基类子对象可以具有零大小。 [剪断]

然后是关于不同地址的要求:

  

8除非对象是零字段或零大小的基类子对象,否则该对象的地址是地址   它占据的第一个字节。两个对象a和b具有重叠的生命周期,而不是位字段可能具有   如果一个嵌套在另一个中,或者如果至少有一个是零大小的基类子对象,则为相同的地址   它们属于不同类型;否则,他们有不同的地址。

空基础子对象和成员数组的第一个元素可能没有相同的地址,因为它们属于同一类型。

因此,如果我们假设必须在成员之前对基础子对象进行排序,并且成员必须处于声明顺序,则需要填充以实现不同的地址。但是,我无法在标准中找到需要此类订单的规则。

便携式 Itanium C ++ ABI(由Clang和GCC使用)确实指定了顺序,在该规范下,确实需要填充:

  

2.4非POD类类型

     

II。除虚拟基地之外的其他成员的分配

     

对于每个数据组件D(首先是C的主要基础,如果有的话,那么是声明顺序中的非主要,非虚拟直接基类,然后是声明顺序中的非静态数据成员和未命名的位域),分配如下:

如果它在内存布局中的基础之前允许非声明顺序子对象或成员子对象,则可以在此处使用不同的ABI。至少作为空基的特殊情况。我不知道这样的ABI是否存在。

答案 1 :(得分:2)

是否可以从规格?是。 [class.mem] / 24告诉我们标准布局类的地址与其第一个基类子对象之间的关系。但是该标准没有对非标准布局类型的这种关系做出声明。由于您的类型不是标准布局,因此标准对该位置一无所知。

基类分配通常有两种ABI形式:将它们放在成员之前或将它们放在成员之后(对于非标准布局类型)。在成员之后分配基类的ABI可以自由地为空基类分配最后的有效地址,并在类的存储区域中具有适当的对齐。如果发生这种情况以避免违反[intro.object] / 8(子对象的唯一标识),那么它就可以执行该分配。

两个最常见的ABI(Windows和Itanium)都在成员之前分配基类。虽然这样的ABI可以更任意地分配基类,但他们并不打扰这样做。无论如何,这只会出现在模板代码中,因为空类为空。它们都表现相同,因此没有理由故意将它们作为基类和成员。

那么,为什么要为一个如此罕见的案例制定复杂的ABI规则呢?