名稱空間
變體
操作

空基類最佳化

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
 
 

允許空基類子物件的尺寸為零。

目錄

[編輯] 解釋

任何物件或成員子物件的尺寸都要求至少為 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));
}

如果存在多重繼承,則具體的最佳化是編譯器特定的。

  • 在 MSVC 中,空基類最佳化僅應用於最後一個空基類,其餘空基類不應用空基類最佳化,並分配一個位元組。
  • 在 GCC 中,無論有多少空基類,空基類都應用空基類最佳化,不分配任何空間,並且空基類的地址與派生類物件的第一個地址相同。

對於標準佈局型別,空基類最佳化是必需的,以保持透過 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::vectorstd::functionstd::shared_ptr 等)使用,以避免在其分配器是無狀態時為分配器成員佔用任何額外儲存。這是透過將其中一個必需的資料成員(例如,vectorbeginendcapacity 指標)儲存在與分配器等價的 boost::compressed_pair 中來實現的。

[編輯] 參考

  • 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)

[編輯] 外部連結

更多 C++ 習語/空基類最佳化 — 一個維基書