Namespaces
Variants

Empty base optimization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Позволяет размеру пустого базового подобъекта быть нулевым.

Содержание

Объяснение

Размер любого объекта или члена-подобъекта должен быть не менее 1, даже если тип является пустым классом (то есть классом или структурой без нестатических членов данных), (если не используется [[ no_unique_address ]] , см. ниже) (начиная с C++20) чтобы гарантировать, что адреса различных объектов одного типа всегда различаются.

Однако подобные ограничения не распространяются на подобъекты базовых классов, которые могут быть полностью исключены из структуры объекта:

struct Base {}; // empty class
struct Derived1 : Base
{
    int i;
};
int main()
{
    // the size of any object of empty class type is at least 1
    static_assert(sizeof(Base) >= 1);
    // empty base optimization applies
    static_assert(sizeof(Derived1) == sizeof(int));
}

Оптимизация пустого базового класса запрещена, если один из пустых базовых классов также является типом или базовым типом первого нестатического члена данных, поскольку требуется, чтобы два базовых подобъекта одного типа имели разные адреса в представлении объекта самого производного типа.

Типичным примером такой ситуации является наивная реализация std::reverse_iterator (унаследованная от пустого базового класса std::iterator ), которая хранит базовый итератор (также унаследованный от std::iterator ) в качестве своего первого нестатического члена данных.

struct Base {}; // empty class
struct Derived1 : Base
{
    int i;
};
struct Derived2 : Base
{
    Base c; // Base, occupies 1 byte, followed by padding for i
    int i;
};
struct Derived3 : Base
{
    Derived1 c; // derived from Base, occupies sizeof(int) bytes
    int i;
};
int main()
{
    // empty base optimization does not apply,
    // base occupies 1 byte, Base member occupies 1 byte
    // followed by 2 bytes of padding to satisfy int alignment requirements
    static_assert(sizeof(Derived2) == 2*sizeof(int));
    // empty base optimization does not apply,
    // base takes up at least 1 byte plus the padding
    // to satisfy alignment requirement of the first member (whose
    // alignment is the same as int)
    static_assert(sizeof(Derived3) == 3*sizeof(int));
}

Оптимизация пустого базового класса обязательна для StandardLayoutType s для сохранения требования, чтобы указатель на объект стандартного размещения, преобразованный с помощью reinterpret_cast , указывал на его начальный член, поэтому требования к типу стандартного размещения включают "имеет все нестатические члены данных, объявленные в одном классе (либо все в производном, либо все в некотором базовом)" и "не имеет базовых классов того же типа, что и первый нестатический член данных".

(начиная с C++11)

Пустые члены-подобъекты могут быть оптимизированы так же, как и пустые базовые классы, если они используют атрибут [[ no_unique_address ]] . Взятие адреса такого члена может дать адрес, равный адресу некоторого другого члена того же объекта.

struct Empty {}; // empty class
struct X
{
    int i;
    [[no_unique_address]] Empty e;
};
int main()
{
    // the size of any object of empty class type is at least 1
    static_assert(sizeof(Empty) >= 1);
    // empty member optimized out:
    static_assert(sizeof(X) == sizeof(int));
}
(начиная с C++20)

Примечания

Оптимизация пустого базового класса обычно используется классами стандартной библиотеки с поддержкой аллокаторов ( std::vector , std::function , std::shared_ptr , и т.д.) чтобы избежать занятия дополнительной памяти для члена-аллокатора, если аллокатор не имеет состояния. Это достигается путем хранения одного из требуемых членов данных (например, begin , end , или capacity указатель для vector ) в эквиваленте boost::compressed_pair с аллокатором.

В MSVC оптимизация пустого базового класса не полностью соответствует требованиям стандарта ( Почему оптимизация пустого базового класса (EBO) не работает в MSVC? ).

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
  • 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)

Внешние ссылки

More C++ Idioms/Empty Base Optimization — викиучебник