30 / 80 · C++20 · 约 8 分钟
空类、EBO 与 [[no_unique_address]]
空类的完整对象仍具有非零大小,以支持对象身份和数组寻址;空基类子对象则可能不占额外空间。C++20 的 no_unique_address 将重叠布局机会扩展到成员,但不保证具体 sizeof,也不取消同类型对象的身份规则。
空的是状态,不是类型语义
没有非静态数据成员的类仍可以有成员函数、静态数据、类型别名和构造析构行为。对于普通空类,完整对象的 sizeof 至少为一,不保证必须等于一;数组中相邻元素必须能作为不同对象被寻址,因此不能把一组空对象都折叠成同一个数组元素。
带虚函数的类虽然也可能没有显式数据字段,却不属于通常用于空对象优化的那类 empty type。标准类型特征 is_empty 判断的也不是源码里有没有写 int 字段。空策略类型常用于比较器、分配策略和编译期标签,它们不携带状态仍有实际用途。
空基类优化与组合的表达力
空基类子对象不受普通完整对象最小占位的同样约束,实现在允许的布局下可以让它不增加额外空间,这称为空基类优化 EBO。一些标准布局条件还对地址关系提出要求,但不能把所有继承结构都简化成固定的大小等式。
如果复用关系仅仅是“拥有一个策略”,用继承只为节约空成员空间容易扭曲接口。C++20 的 [[no_unique_address]] 可标记非静态非位域成员,使它成为潜在重叠子对象;空策略因而有机会与其他成员共享地址,同时仍保持清晰的组合关系。
允许重叠不等于保证压缩
属性允许实现利用布局机会,不承诺指定的对象大小,也不保证成员一定与别的成员同地址。它还可能允许复用非空成员的尾部填充,所以不能自行把该成员的整个 sizeof 字节区域当作独占存储随意覆盖。
同一对象内两个同类型空成员仍需满足地址区分规则,不能因为都加了属性就认为可完全重合。示例只断言空完整对象非零、同类型空成员地址不同以及策略调用正确,不断言外层一定等于 int 大小。空间收益应在目标编译器与 ABI 上测量,并作为实现结果记录。
容易答错的地方
- sizeof(Empty) 常为一只是常见结果;对齐要求等因素可以使它更大,跨平台代码不应硬编码。
- no_unique_address 不延长生命周期,也不让你可以绕开对象规则覆盖共享存储;它是布局属性,不是手工内存管理许可证。
运行一个例子
最低标准 C++20 · 完整程序 · 下载 .cpp
#include <cassert>
#include <type_traits>
struct Empty {};
struct Identity {
int operator()(int value) const { return value; }
};
struct ViaBase : Empty {
int value = 3;
};
struct WithPolicy {
[[no_unique_address]] Identity policy;
int value = 7;
int evaluate() const { return policy(value); }
};
struct TwoTags {
[[no_unique_address]] Empty first;
[[no_unique_address]] Empty second;
int value = 1;
};
int main() {
static_assert(std::is_empty_v<Empty>);
static_assert(sizeof(Empty) >= 1);
Empty array[2];
assert(&array[0] != &array[1]);
ViaBase inherited;
WithPolicy composed;
TwoTags tags;
assert(inherited.value == 3);
assert(composed.evaluate() == 7);
assert(&tags.first != &tags.second);
}
在本地编译
g++ -std=c++20 -Wall -Wextra -Wpedantic -pthread objects-empty-class.cpp -o example && ./example预期结果
预期:正常退出、无输出;所有 assert 通过。
CHECK YOUR UNDERSTANDING
合上答案,试着解释。
WithPolicy 的 sizeof 在某编译器上等于 sizeof(int),能否写成库的跨平台 static_assert?
查看参考答案
不应这样做。该观察说明此目标实现利用了空成员重叠,但属性允许而不强制这一具体布局。跨平台断言应约束真实语义,如策略调用结果;若库确有严格 ABI 大小要求,应限定支持平台、编译器与构建配置,并把大小检查视为该平台的专门契约,而不是 C++20 的一般保证。
继续查证
标准草案链接会随工作草案更新;本文版本标记对应示例最低要求,不表示草案中的所有新规则都适用于旧标准。