Оптимизация пустой базы - это здорово. Тем не менее, он имеет следующие ограничения:
Оптимизация пустой базы запрещена, если один из пустых базовых классов также является типом или базой типа первого не статического члена данных, поскольку два базовых подобъекта одного типа должны иметь разные адреса в представлении объекта. самого производного типа.
Чтобы объяснить это ограничение, рассмотрим следующий код. Не static_assert
удастся. Принимая во внимание, что изменение Foo
или Bar
наследования вместо Base2
этого предотвратит ошибку:
#include <cstddef>
struct Base {};
struct Base2 {};
struct Foo : Base {};
struct Bar : Base {
Foo foo;
};
static_assert(offsetof(Bar,foo)==0,"Error!");
Я полностью понимаю это поведение. Чего я не понимаю, так это того, почему существует такое поведение . Это было очевидно добавлено по причине, так как это явное дополнение, а не недосмотр. Что является обоснованием для этого?
В частности, почему два базовых подобъекта должны иметь разные адреса? Выше, Bar
является типом и foo
является переменной-членом этого типа. Я не понимаю, почему базовый класс имеет Bar
значение для базового класса типа foo
или наоборот.
Действительно, я, если что-нибудь, я ожидал бы, что &foo
это то же самое, что и адрес Bar
экземпляра, содержащего его, - как это требуется в других ситуациях (1) . В конце концов, я не делаю ничего сложного с virtual
наследованием, базовые классы пусты независимо, и компиляция с Base2
показывает, что в этом конкретном случае ничего не ломается.
Но ясно, что это рассуждение как-то неверно, и есть другие ситуации, когда это ограничение будет необходимо.
Допустим, ответы должны быть для C ++ 11 или новее (в настоящее время я использую C ++ 17).
(1) Примечание: EBO был обновлен в C ++ 11 и, в частности, стал обязательным для StandardLayoutType
s (хотя Bar
, выше, это не a StandardLayoutType
).
источник
Base *a = new Bar(); Base *b = a->foo;
сa==b
, ноa
иb
явно разные объекты (возможно , с разными переопределяет виртуальный метод).Ответы:
Хорошо, похоже, что я все время ошибался, так как для всех моих примеров должна существовать vtable для базового объекта, который не позволил бы начинать оптимизацию пустой базы. Я оставлю примеры в силе, так как думаю, что они дают некоторые интересные примеры того, почему уникальные адреса обычно полезны.
Изучив все это более подробно, нет технической причины, по которой оптимизация пустого базового класса будет отключена, если первый член того же типа, что и пустой базовый класс. Это просто свойство текущей объектной модели C ++.
Но в C ++ 20 появится новый атрибут,
[[no_unique_address]]
который сообщает компилятору, что нестатическому члену данных может не понадобиться уникальный адрес (технически говоря, он потенциально перекрывается [intro.object] / 7 ).Это подразумевает, что (выделение мое)
следовательно, можно «реактивировать» пустую оптимизацию базового класса, дав первому члену данных атрибут
[[no_unique_address]]
. Я добавил здесь пример, который показывает, как это (и все другие случаи, которые я мог придумать) работает.Неправильные примеры проблем через это
Поскольку кажется, что пустой класс может не иметь виртуальных методов, позвольте мне добавить третий пример:
Но последние два звонка одинаковы.
Старые примеры (вероятно, не отвечают на вопрос, так как пустые классы, возможно, не содержат виртуальных методов)
Рассмотрите в своем коде выше (с добавленными виртуальными деструкторами) следующий пример
Но как компилятор должен различать эти два случая?
И, может быть, немного менее надуманным:
Но последние два одинаковы, если у нас пустая оптимизация базового класса!
источник
std::is_empty
на cppreference гораздо сложнее. То же из текущего проекта на eel.is .dynamic_cast
когда это не полиморфно (с незначительными исключениями, не относящимися к делу).