Оптимизация пустого базового класса
Позволяет размеру пустого базового подобъекта быть равным нулю.
Объяснение
Размер любого объекта или подобъекта-элемента должен быть не менее 1, даже если тип является пустым классовым типом (то есть классом или структурой, не имеющей статических элементов данных), (если не используется [[no_unique_address]], смотрите ниже) (начиная с C++20), чтобы иметь возможность гарантировать, что адреса различных объектов одного и того же типа всегда различны.
Однако подобъекты базового класса не так ограничены и могут быть полностью оптимизированы из макета объекта:
struct Base {}; // пустой класс
struct Derived1 : Base {
int i;
};
int main()
{
// размер любого объекта пустого типа класса не менее 1
static_assert(sizeof(Base) >= 1);
// применяется оптимизация пустого базового класса
static_assert(sizeof(Derived1) == sizeof(int));
}
Оптимизация пустой базы запрещена, если один из пустых базовых классов также является типом или базой типа первого нестатического элемента данных, поскольку два базовых подобъекта одного и того же типа должны иметь разные адреса в представлении объекта наиболее производного типа.
Типичным примером такой ситуации является наивная реализация std::reverse_iterator (производного от пустого базового класса std::iterator), которая содержит базовый итератор (также полученный из std::iterator) в качестве первого нестатического элемента данных.
struct Base {}; // пустой класс
struct Derived1 : Base {
int i;
};
struct Derived2 : Base {
Base c; // Base, занимает 1 байт, за которым следует заполнитель для i
int i;
};
struct Derived3 : Base {
Derived1 c; // производный от Base, занимает sizeof(int) байт
int i;
};
int main()
{
// оптимизация пустой базы не применяется, база занимает 1 байт,
// элемент Base занимает 1 байт, за которым следуют 2 байта заполнения
// для удовлетворения требований выравнивания int
static_assert(sizeof(Derived2) == 2*sizeof(int));
// оптимизация пустой базы не применяется, база занимает не менее 1 байта
// плюс заполнение, чтобы удовлетворить требование выравнивания первого элемента
// (чьё выравнивание такое же, как int)
static_assert(sizeof(Derived3) == 3*sizeof(int));
}
Если происходит множественное наследование, то конкретные оптимизации зависят от компилятора. В MSVC оптимизация нулевого базового класса применяется только с последнему нулевому базовому классу, к остальным нулевым базовым классам нулевая базовая оптимизация не применяется, и выделяется один байт. В GCC, независимо от того, сколько существует пустых базовых классов, оптимизация пустого базового класс применяет к пустым базовым классам без выделения памяти, а адрес пустого базового класса совпадает с адресом первого объекта производного класса.
|
Оптимизация пустой базы требуется для StandardLayoutType, чтобы сохранить требование, когда указатель на объект стандартной компоновки, преобразованный с использованием |
(начиная с C++11) |
|
Пустые подобъекты-элементы можно оптимизировать так же, как и пустые базовые классы, если они используют атрибут Запустить этот код struct Empty {}; // empty class
struct X {
int i;
[[no_unique_address]] Empty e;
};
int main()
{
// размер любого объекта пустого классового типа не менее 1
static_assert(sizeof(Empty) >= 1);
// пустой элемент оптимизирован:
static_assert(sizeof(X) == sizeof(int));
}
|
(начиная с C++20) |
Примечание
Оптимизация пустой базы обычно используется классами стандартной библиотеки с поддержкой аллокатора (std::vector, std::function, std::shared_ptr и т.д.) чтобы не занимать какую-либо дополнительную память для своего элемента аллокатора, если аллокатор не имеет состояния. Это достигается путём сохранения одного из необходимых элементов данных (например, указателя begin, end или capacity для vector) в эквивалент boost::compressed_pair с распределителем.
Ссылки
- C++23 стандарт (ISO/IEC 14882:2023):
- 7.6.10 Операторы равенства [expr.eq]
- 7.6.2.5 Sizeof [expr.sizeof]
- 11 Классы [class]
- 11.4 Элементы класса [class.mem]
- C++20 стандарт (ISO/IEC 14882:2020):
- 7.6.10 Операторы равенства [expr.eq]
- 7.6.2.4 Sizeof [expr.sizeof]
- 11 Классы [class]
- 11.4 Элементы класса [class.mem]
- C++17 стандарт (ISO/IEC 14882:2017):
- 8.10 Операторы равенства [expr.eq]
- 8.3.3 Sizeof [expr.sizeof]
- 12 Классы [class]
- 12.2 Элементы класса [class.mem]
- C++14 стандарт (ISO/IEC 14882:2014):
- 5.10 Операторы равенства [expr.eq]
- 5.3.3 Sizeof [expr.sizeof]
- 9 Классы [class]
- 9.2 Элементы класса [class.mem]
- C++11 стандарт (ISO/IEC 14882:2011):
- 5.10 Операторы равенства [expr.eq](стр. 2)
- 5.3.3 Sizeof [expr.sizeof](стр. 2)
- 9 Классы [class](стр. 4,7)
- 9.2 Элементы класса [class.mem](стр. 20)
- C++98 стандарт (ISO/IEC 14882:1998):
- 5.10 Операторы равенства [expr.eq](стр. 2)
- 5.3.3 Sizeof [expr.sizeof](стр. 2)
- 9 Классы [class](стр. 3)
Внешние ссылки
| Дополнительные Идиомы C++/Оптимизация Пустой Базы — Викикнига |