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));
}

空の基底クラスの1つが最初の非静的データメンバーの型またはその基底型でもある場合、空の基底クラス最適化は禁止されます。なぜなら、同じ型の2つの基底サブオブジェクトは、最も派生した型のオブジェクト表現内で異なるアドレスを持つことが要求されるためです。

このような状況の典型的な例は、 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バイトを占有
    // その後intのアライメント要件を満たすための2バイトのパディングが続く
    static_assert(sizeof(Derived2) == 2*sizeof(int));
    // 空基底クラス最適化は適用されない、
    // 基底クラスは少なくとも1バイトと、最初のメンバの
    // アライメント要件を満たすためのパディングを占有
    // (そのアライメントは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 など)で一般的に使用され、アロケータがステートレスな場合にアロケータメンバが追加のストレージを占有するのを防ぎます。これは、必要なデータメンバの1つ(例: begin end または capacity ポインタ for vector )をアロケータとの boost::compressed_pair 相当の構造に格納することで実現されます。

MSVCでは、空の基底クラス最適化は標準要件に完全に準拠していません( Why is the empty base class optimization (EBO) is not working in 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] (p: 2)
  • 5.3.3 Sizeof [expr.sizeof] (p: 2)
  • 9 クラス [class] (p: 4,7)
  • 9.2 クラスメンバ [class.mem] (p: 20)
  • C++98標準 (ISO/IEC 14882:1998):
  • 5.10 等価演算子 [expr.eq] (p: 2)
  • 5.3.3 Sizeof演算子 [expr.sizeof] (p: 2)
  • 9 クラス [class] (p: 3)

外部リンク

More C++ Idioms/Empty Base Optimization — ウィキブック