命名空间
变体
操作

空基类优化

来自 cppreference.cn
< cpp‎ | language
 
 
C++ 语言
通用主题
流程控制
条件执行语句
if
迭代语句(循环)
for
范围 for (C++11)
跳转语句
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (直到 C++17*)
noexcept 说明符 (C++11)
异常
命名空间
类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储期说明符
初始化
 
 

允许空基类子对象的大小为零。

目录

[编辑] 解释

任何对象或成员子对象的大小都必须至少为 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 中,无论存在多少个空基类,空基类优化都适用,而无需分配任何空间,并且空基类地址与派生类对象的第一个地址相同。

对于StandardLayoutType要求进行空基类优化,以保持使用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)

[编辑] 外部链接

More C++ Idioms/Empty Base Optimization — 一本维基书