Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does sizeof(D) equal 2 in this code (see details)? [duplicate]

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 759
Bathsheba Avatar asked Jul 04 '17 15:07

Bathsheba


People also ask

Why does sizeof return 4?

Value 100 in the square brackets indicates to the programmer that he is working with an array of 100 items. But it is not an array of a hundred items which is passed into the function - it is only the pointer. So, the sizeof(B) expression will return value 4 or 8 (the size of the pointer in a 32-bit/64-bit system).

Why does the sizeof operator return a size larger for a structure than the total sizes of the structure's members?

The sizeof for a struct is not always equal to the sum of sizeof of each individual member. This is because of the padding added by the compiler to avoid alignment issues. Padding is only added when a structure member is followed by a member with a larger size or at the end of the structure.

What is the purpose of sizeof () Why is called as a special operator?

The sizeof operator applied to a type name yields the amount of memory that can be used by an object of that type, including any internal or trailing padding. The result is the total number of bytes in the array. For example, in an array with 10 elements, the size is equal to 10 times the size of a single element.

What does sizeof () do in C?

What is the sizeof() function in C? The sizeof() function in C is a built-in function that is used to calculate the size (in bytes)that a data type occupies in ​the computer's memory.


4 Answers

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 161
themiurge Avatar answered Oct 13 '22 00:10

themiurge


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 43
Maxim Egorushkin Avatar answered Oct 13 '22 00:10

Maxim Egorushkin


Objects in C++ are required to have unique "identity". From [intro.object]/8 (N4659):

Two objects a and b with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a base class subobject of zero size and they are of different types; otherwise, they have distinct addresses.

A base class subobject and a member subobject are separate objects; neither is "nested within" the other. Therefore, if they are of the same type, they must have separate addresses.

Note that this extends recursively. Consider the following:

struct eb1 {};

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

struct derived : eb2 {not_empty b;};

Because of the unique identity rule of C++, derived::eb2::eb1 must have a different address from derived::b::a. Therefore, the compiler cannot employ EBO on derived.

like image 28
Nicol Bolas Avatar answered Oct 13 '22 00:10

Nicol Bolas


I'll plop in another more basic quote

[intro.object]

Two objects a and b with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a base class subobject of zero size and they are of different types; otherwise, they have distinct addresses.


Since b is not an subobject of the inherited base, they must have distinct addresses.

like image 23
Passer By Avatar answered Oct 13 '22 01:10

Passer By