c++中的std::variant和std::any的性能对比_c++类型安全容器选择【C++17】

19次阅读

c++kquote>std::variant 是编译期确定类型的零开销有限多态容器,上存储、无虚调用;std::any 是运行期类型擦除的无限多态容器,依赖分配、虚函数和类型检查,性能开销显著。

c++中的std::variant和std::any的性能对比_c++类型安全容器选择【C++17】

std::variant 和 std::any 都是 c++17 引入的类型安全容器,但设计目标和性能特征差异显著——variant 是编译期确定类型的“有限多态”,any 是运行期擦除类型的“无限多态”。性能上,variant 几乎无额外开销,any 则有动态内存、虚函数调用和类型信息查找等成本。

内存布局与访问开销

std::variant 在上直接存储其可选类型的**最大尺寸 + 1 字节(用于存放当前索引)**,访问时通过编译期生成的 switch 或跳转表完成,零运行时类型检查开销。例如:

std::variant<int, double, std::String> v = 42; // 访问 int:直接取地址偏移,无虚调用、无 new/delete

std::any 则内部持有一个指向堆内存的指针(除非小对象优化 SOO 生效),且每次访问需:

  • 调用 typeid 对比判断类型是否匹配
  • 若匹配,再解引用并 static_cast
  • 构造/析构过程涉及虚函数(如 std::any::holder 的 ~holder())

构造与赋值代价

variant 构造是 trivial 的(若所含类型都 trivial),即使非 trivial 类型,也只调用对应分支的构造函数,无额外抽象层。

立即学习C++免费学习笔记(深入)”;

any 的构造必然触发一次堆分配(除非 SOO 触发),且需保存类型信息(std::type_info*)和拷贝函数指针;赋值还可能引发新分配+旧释放。例如:

c++中的std::variant和std::any的性能对比_c++类型安全容器选择【C++17】

Magician

Figma插件,AI生成图标、图片和UX文案

c++中的std::variant和std::any的性能对比_c++类型安全容器选择【C++17】 487

查看详情 c++中的std::variant和std::any的性能对比_c++类型安全容器选择【C++17】

std::any a = std::string(1000, 'x'); // 很可能堆分配 a = 3.14; // 原 string 析构 + 新 double 存储(栈上,但需类型擦除逻辑)

适用场景决定性能取舍

选 variant 当你明确知道所有可能类型,且希望零成本抽象:

  • 解析器返回值(int/double/string/Error
  • 状态机状态(idle/running/paused)
  • AST 节点子类型集合固定

选 any 当你需要真正泛化的容器,比如插件系统参数、配置项、反射字段值:

  • 任意类型值的 map:std::map<:string std::any>
  • 回调函数参数打包
  • 无法在编译期枚举全部类型的场景

小对象优化(SOO)的影响

some std::any 实现(如 libstdc++ 和 MSVC)支持 SOO:对 sizeof ≤ 约 16–32 字节且无抛出析构的类型,直接存栈上,避免堆分配。但这不改变其运行时类型查询和虚函数调用的本质开销。

variant 没有 SOO 概念——它天生栈驻留,大小和访问路径完全静态可知。即使含 std::string,variant 仍只预留 max_size,不触发任何动态行为(除非你主动调用 string 的构造函数)。

基本上就这些。不是“哪个更快”,而是“哪个更合适”——variant 快得理所当然,any 慢得情有可原。用错场景,再快的 any 也救不了设计缺陷;用对地方,variant 的零成本就是最硬的性能保障。

text=ZqhQzanResources