
std::variant 和 std::any 都是 C++17 引入的类型安全容器,但设计目标和性能特征差异显著——variant 是编译期确定类型的“有限多态”,any 是运行期擦除类型的“无限多态”。性能上,variant 几乎无额外开销,any 则有动态内存、虚函数调用和类型信息查找等成本。
内存布局与访问开销
std::variant 在栈上直接存储其可选类型的**最大尺寸 + 1 字节(用于存放当前索引)**,访问时通过编译期生成的 switch 或跳转表完成,零运行时类型检查开销。例如:
std::variantv = 42; // 访问 int:直接取地址偏移,无虚调用、无 new/delete
std::any 则内部持有一个指向堆内存的指针(除非小对象优化 SOO 生效),且每次访问需:
- 调用 typeid 对比判断类型是否匹配
- 若匹配,再解引用并 static_cast
- 构造/析构过程涉及虚函数(如 std::any::holder 的 ~holder())
构造与赋值代价
variant 构造是 trivial 的(若所含类型都 trivial),即使非 trivial 类型,也只调用对应分支的构造函数,无额外抽象层。
立即学习“C++免费学习笔记(深入)”;
any 的构造必然触发一次堆分配(除非 SOO 触发),且需保存类型信息(std::type_info*)和拷贝函数指针;赋值还可能引发新分配+旧释放。例如:
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 的零成本就是最硬的性能保障。









