C++中的空基类优化

2025-4-20|2025-4-20
FollyCoolly
FollyCoolly
type
status
date
slug
summary
tags
category
icon
password

什么是空基类优化

空基类优化,即允许空的基类子对象大小为零
这有什么特殊的地方,值得拿出来说呢?因为根据 C++ 标准,为保证同一类型的不同对象地址始终有别,要求任何对象或成员子对象的大小至少为 1,即使该类型是空的类类型(即没有非静态数据成员的类或结构体)(除非它带有 [[no_unique_address]]
然而,基类子对象不受这种制约,而且可以完全从对象布局中被优化掉。
注意,虚类不能应用空基类优化,因为它至少要包含一i个虚表,肯定不为空。

特殊情况

如果首个非静态数据成员的类型与一个空基类的类型相同或者由该空基类派生,那么禁用空基类优化,因为 C++ 标准要求两个同类型基类子对象在最终派生类型的对象表示中必须拥有不同地址
这种情况的典例是 std::reverse_iterator 的朴素实现(派生自空基类 std::iterator),它持有其底层迭代器(同样派生自 std::iterator)为其首个非静态数据成员。
 
C++要求只有不同类型的空基类才能被EBCO优化。若空类类型相同且分布在继承链中,优化将失败:
通过将空类放在开头并确保类型唯一(如通过模板包装),可规避此问题:

[[no_unique_address]]

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

MSVC 中的空基类优化

在 MSVC 中,空基类优化并不完全符合标准:

STL 中的应用

std::unique_ptr 的 deleter

std::unique_ptr的删除器(Deleter)如果是空类,不会增加指针大小:
 
std::unique_ptr 的完整声明如下:
为了实现零开销抽象,std::unique_ptr 将删除器作为基类而非成员变量。如果 Deleter 是空类(如 std::default_delete),基类继承触发 EBCO,Deleter 不占用额外内存。

std::tuple

std::tuple会压缩空类型的存储:
 
std::tuple 的典型实现通过递归继承将每个元素存储为基类:
 
当 tuple 包含多个空类时,需确保它们的基类类型唯一,否则编译器可能无法优化:
 
浅浅浅谈compile_commands.jsonC++中的参数依赖查找(ADL)
Loading...