Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can the compiler exploit empty base optimisation if the class contains a member of the base class?

Consider

struct base {};
struct child : base {};

It's well-known that sizeof(child) can be 1 by application of the empty base optimisation.

Now however, consider

struct base {};
struct child : base {base b;};

Can the compiler apply the empty base optimisation now, or must sizeof(child) be at least 2?

Reference: http://en.cppreference.com/w/cpp/language/ebo

like image 312
Bathsheba Avatar asked Jul 04 '17 15:07

Bathsheba


People also ask

What is empty base class optimization (EBCO)?

The C++ Standard requires that a most-derived object must have a non-zero size and must occupy one or more bytes of storage. Because the requirement only extends to most-derived objects, base class subobjects aren't subject to this constraint. The Empty Base Class Optimization (EBCO) takes advantage of this liberty.

Does the base class size change when the derived class is empty?

However, when the base class is empty, its size is not necessarily added to the derived class: In this case, it's not required to allocate a byte for Base within Derived to have a distinct address per type per object.

Are empty member subobjects allowed to be optimized?

The empty member subobjects are permitted to be optimized out just like the empty bases if they use the attribute [ [ no_unique_address ]]. Taking the address of such member results in an address that may equal the address of some other member of the same object.

Do we need to allocate a byte for an empty base?

In this case, it's not required to allocate a byte for Base within Derived to have a distinct address per type per object. If empty base class optimization is performed (and no padding is required), then sizeof (Derived) == sizeof (int), that is, no additional allocation is done for the empty base.


2 Answers

The rule is that sub-objects of the same type cannot be at the same address. You have 2 X sub-objects here, hence each one must be at a different address.

Objects of the same type cannot share the same address because the identity of an object in C++ is its address. If multiples objects of the same type share the same address they are indistinguishable. And this is why the minimum complete object size is 1, so that each object in an array has a distinct address. See "§ The C++ object model [intro.object]":

An object is a region of storage.

...

Objects can contain other objects, called subobjects. A subobject can be a member subobject (9.2), a base class subobject (Clause 10), or an array element. An object that is not a subobject of any other object is called a complete object.

...

Unless it is a bit-field (9.6), a most derived object shall have a non-zero size and shall occupy one or more bytes of storage. Base class subobjects may have zero size.

...

Unless an object is a bit-field or a base class subobject of zero size, the address of that object is the address of the first byte it occupies. Two objects that are not bit-fields may have the same address if one is a subobject of the other, or if at least one is a base class subobject of zero size and they are of different types; otherwise, they shall have distinct addresses.

This is why, for example, boost::noncopyable can increase a class size if one inherits it more than once unwittingly indirectly through empty base classes. E.g. in:

struct A : boost::noncopyable {};
struct B : boost::noncopyable {};
struct C : boost::noncopyable {};
struct D : A, B, C {};

sizeof(D) == 3 because there are three distinct boost::noncopyable sub-subjects. If derivation from boost::noncopyable is dropped, then sizeof(D) == 1.

like image 169
Maxim Egorushkin Avatar answered Sep 24 '22 13:09

Maxim Egorushkin


No, it cannot. From the same reference:

Empty base optimization is prohibited if one of the empty base classes is also the type or the base of the type of the first non-static data member

Thus sizeof(child) >= 2.

like image 41
themiurge Avatar answered Sep 24 '22 13:09

themiurge