空基类优化

来自cppreference.com
< cpp‎ | language

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

解释

为保证同一类型的不同对象地址始终有别,要求任何对象或成员子对象的大小至少为 1,即使该类型是空的类类型(即没有非静态数据成员的 class 或 struct)(,除非带有 [[no_unique_address]],见下文) (C++20 起)

然而,基类子对象不受这种制约,而且可以完全从对象布局中被优化掉:

#include <cassert>
 
struct Base {}; // 空类
 
struct Derived1 : Base {
    int i;
};
 
int main()
{
    // 任何空类类型的对象大小至少为 1
    assert(sizeof(Base) > 0);
 
    // 应用空基类优化
    assert(sizeof(Derived1) == sizeof(int));
}

如果首个非静态数据成员的类型与一个空基类的类型相同或者由该空基类派生,那么禁用空基类优化,因为要求两个同类型基类子对象在最终派生类型的对象表示中必须拥有不同地址。

这种情况的典例是 std::reverse_iterator 的朴素实现(派生自空基类 std::iterator),它持有其底层迭代器(同样派生自 std::iterator)为其首个非静态数据成员。

#include <cassert>
 
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 的对齐要求
    assert(sizeof(Derived2) == 2*sizeof(int));
 
    // 不应用空基类优化,
    // 基类占用至少 1 字节加填充,
    // 以满足首个成员的对齐要求(其对齐要求与 int 相同)
    assert(sizeof(Derived3) == 3*sizeof(int));
}

如果发生多重继承,那么具体的优化措施是特定于编译器的。 在 MSVC 中,有且仅有最后一个空基类应用空基类优化,其余空基类均不应用空基类优化,并分配一个字节。 在 GCC 中,无论存在多少个空基类,空基类均应用空基类优化,不分配任何空间,空基类地址与派生类对象首地址相同。

标准布局类型 (StandardLayoutType) 必须应用空基类优化以维持指向标准布局对象的指针在用 reinterpret_cast 转换后还指向其首成员,这是标准要求标准布局类型“在同一个类中声明所有非静态数据成员(全在派生类或全在某个基类)”,并且“不能有与首个非静态数据成员类型相同的基类”的原因。

(C++11 起)

如果空成员子对象使用属性 [[no_unique_address]],那么允许像空基类一样优化掉它们。取这种成员的地址会产生可能等于同一个对象的某个其他成员的地址。

#include <cassert>
 
struct Empty {}; // 空类
 
struct X {
    int i;
    [[no_unique_address]] Empty e;
};
 
int main()
{
    // 任何空类类型对象的大小至少为 1
    assert(sizeof(Empty) >= 1);
 
    // 空成员被优化掉
    assert(sizeof(X) == sizeof(int));
}
(C++20 起)

注解

空基类优化常用于具分配器的标准库类(std::vectorstd::functionstd::shared_ptr 等),使得当分配器无状态时可避免为其分配器成员占用任何额外存储。这是通过将必要的数据成员之一(例如 vector 的 begin、end 或 capacity 指针)与分配器一起,在 boost::compressed_pair 的某种等价物中存储而实现的。

引用

  • C++11 标准(ISO/IEC 14882:2011):
  • 5.10 Equality operators [expr.eq](第 2 页)
  • 5.3.3 Sizeof [expr.sizeof](第 2 页)
  • 9 Classes [class](第 4,7 页)
  • 9.2 Class members [class.mem](第 20 页)
  • C++98 标准(ISO/IEC 14882:1998):
  • 5.10 Equality operators [expr.eq](第 2 页)
  • 5.3.3 Sizeof [expr.sizeof](第 2 页)
  • 9 Classes [class](第 3 页)