如果类包含基类的成员,编译器是否可以利用空基优化?

Bat*_*eba 13 c++ language-lawyer

考虑

struct base {};
struct child : base {};
Run Code Online (Sandbox Code Playgroud)

众所周知,sizeof(child)通过应用空基优化可以是1 .

但是现在考虑一下

struct base {};
struct child : base {base b;};
Run Code Online (Sandbox Code Playgroud)

编译器现在可以应用空基优化,还是必须sizeof(child)至少为2?

参考:http://en.cppreference.com/w/cpp/language/ebo

the*_*rge 18

不,它不能.来自同一参考:

如果其中一个空基类也是第一个非静态数据成员的类型或基类,则禁止空基本优化

因此sizeof(child) >= 2.

  • IMO,标记为[language-lawyer]的问题应该根据实际规范的引用来回答.虽然cppreference非常好,但它并不是C++如何工作的实际定义,而且过去也需要错误. (4认同)

Max*_*kin 16

规则是相同类型的子对象不能在同一地址.X这里有2 个子对象,因此每个子对象必须位于不同的地址.

由于C++中对象的标识是其地址,因此相同类型的对象不能共享相同的地址.如果相同类型的多个对象共享相同的地址,则它们是无法区分的.这就是最小完整对象大小为1的原因,因此数组中的每个对象都有一个不同的地址.请参阅"§C++对象模型[intro.object]":

对象是存储区域.

...

对象可以包含其他对象,称为子对象.子对象可以是成员子对象(9.2),基类子对象(子句10)或数组元素.不是任何其他对象的子对象的对象称为完整对象.

...

除非它是位字段(9.6),否则最派生的对象应具有非零大小并且应占用一个或多个存储字节.基类子对象可以具有零大小.

...

除非对象是零字段或零大小的基类子对象,否则该对象的地址是它占用的第一个字节的地址.如果一个是另一个的子对象,或者如果至少一个是零大小的基类子对象并且它们是不同类型的,则不是位字段的两个对象可以具有相同的地址; 否则,他们应有不同的地址.

这就是为什么,例如,boost::noncopyable如果通过空基类间接地无意间地继承它,可以增加类的大小.例如:

struct A : boost::noncopyable {};
struct B : boost::noncopyable {};
struct C : boost::noncopyable {};
struct D : A, B, C {};
Run Code Online (Sandbox Code Playgroud)

sizeof(D) == 3因为有三个不同的boost::noncopyable子科目.如果boost::noncopyable删除派生,那么sizeof(D) == 1.


Nic*_*las 14

C++中的对象需要具有唯一的"身份".来自[intro.object]/8(N4659):

如果一个对象嵌套在另一个对象中,或者如果至少有一个是零大小的基类子对象并且它们的类型不同,那么两个对象a并且b具有不是位字段的重叠生存期可以具有相同的地址; 否则,他们有不同的地址.

基类子对象和成员子对象是单独的对象; 两者都没有"嵌套"在另一个之内.因此,如果它们属于同一类型,则它们必须具有单独的地址.

请注意,这会递归地延伸.考虑以下:

struct eb1 {};

struct eb2 : eb1 {};
struct not_empty(eb1 a;};

struct derived : eb2 {not_empty b;};
Run Code Online (Sandbox Code Playgroud)

由于C++的唯一标识规则,derived::eb2::eb1 必须有不同的地址derived::b::a.因此,编译器不能使用EBO derived.


Pas*_* By 6

我将推出另一个更基本的报价

[intro.object]

如果一个嵌套在另一个中,则具有不是位字段的重叠生存期的两个对象a和b可以具有相同的地址,或者如果至少一个是零大小的基类子对象并且它们具有不同类型,则它们可以具有相同的地址.否则,他们有不同的地址.


由于b不是继承的子对象base,因此它们必须具有不同的地址.