Ответ 1
Нет, не может. Из той же справки:
Пустая оптимизация базы запрещена, если один из пустых базовых классов также является типом или базой типа первых нестатических данных Член
Таким образом, sizeof(child) >= 2
.
Рассмотрим
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
Нет, не может. Из той же справки:
Пустая оптимизация базы запрещена, если один из пустых базовых классов также является типом или базой типа первых нестатических данных Член
Таким образом, sizeof(child) >= 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
.
Объекты на С++ должны иметь уникальную "идентификацию". Из [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
.
Я напишу еще одну базовую цитату
Два объекта a и b с перекрывающимися временами жизни, которые не являются битовыми полями, могут иметь один и тот же адрес, если один из них вложен в другой, или если хотя бы один является подобъектом базового класса с нулевым размером и они имеют разные типы; в противном случае они имеют разные адреса.
Так как b
не является подобъектом унаследованного base
, они должны иметь разные адреса.