Может ли компилятор использовать пустую базовую оптимизацию, если класс содержит член базового класса?

Рассмотрим

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

Хорошо известно, что sizeof(child) может быть 1 путем применения пустой оптимизации базы.

Теперь, однако, рассмотрим

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

Может ли компилятор применить пустую базовую оптимизацию или должен sizeof(child) быть не менее 2?

Ссылка: http://en.cppreference.com/w/cpp/language/ebo

Ответ 1

Нет, не может. Из той же справки:

Пустая оптимизация базы запрещена, если один из пустых базовых классов также является типом или базой типа первых нестатических данных Член

Таким образом, sizeof(child) >= 2.

Ответ 2

Правило состоит в том, что под-объекты одного и того же типа не могут иметь один и тот же адрес. Здесь у вас есть 2 X под-объекта, поэтому каждый из них должен иметь другой адрес.

Объекты одного и того же типа не могут использовать один и тот же адрес, поскольку идентификатор объекта в С++ является его адресом. Если множественные объекты одного и того же типа имеют один и тот же адрес, они неразличимы. И поэтому минимальный полный размер объекта равен 1, так что каждый объект в массиве имеет отдельный адрес. См. "§ Объектная модель С++ [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 {};

sizeof(D) == 3, потому что существует три различных подтекста boost::noncopyable. Если вывод из boost::noncopyable отбрасывается, то sizeof(D) == 1.

Ответ 3

Объекты на С++ должны иметь уникальную "идентификацию". Из [intro.object]/8 (N4659):

Два объекта a и b с перекрывающимися сроками службы, которые не являются битовыми полями, могут иметь один и тот же адрес, если один из них вложен в другой, или если хотя бы один является подобъектом базового класса с нулевым размером, и они разных типов; в противном случае они имеют разные адреса.

Субобъект базового класса и подобъект-член являются отдельными объектами; ни один из них не "вложен в" другой. Поэтому, если они одного типа, они должны иметь отдельные адреса.

Обратите внимание, что это продолжается рекурсивно. Рассмотрим следующее:

struct eb1 {};

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

struct derived : eb2 {not_empty b;};

Из-за уникального правила идентификации С++, derived::eb2::eb1 должен иметь другой адрес от derived::b::a. Поэтому компилятор не может использовать EBO на derived.

Ответ 4

Я напишу еще одну базовую цитату

[intro.object]

Два объекта a и b с перекрывающимися временами жизни, которые не являются битовыми полями, могут иметь один и тот же адрес, если один из них вложен в другой, или если хотя бы один является подобъектом базового класса с нулевым размером и они имеют разные типы; в противном случае они имеют разные адреса.


Так как b не является подобъектом унаследованного base, они должны иметь разные адреса.