std::variant 是类型安全的编译期确定联合体,不是多态机制;它在运行时仅存一种已知类型,通过 index() 或 holds_alternative() 跟踪活跃类型,适用于 JSON 解析等有限类型场景。

std::variant 是什么,不是什么
std::variant 不是多态机制,它不依赖虚函数表,也不要求类型间有继承关系。它本质是一个**类型安全的联合体(union)**,在编译期确定所有可能类型,运行时仅存其中一种,并用内部索引(index())或类型标签(holds_alternative)跟踪当前活跃类型。
它解决的问题很具体:需要一个变量能“在几个已知类型中选一个”并保证访问安全,比如解析 JSON 时字段可能是 int、double 或 std::string,但绝不会是用户自定义未声明的类型。
怎么正确构造和访问 variant
构造时必须明确初始化为某一个可选项,不能留空(除非含 std::monostate);访问必须通过 std::visit 或 std::get,且后者在类型不匹配时抛 std::bad_variant_access 异常。
- 推荐用
std::visit+ lambda 处理所有分支,避免手动检查index()或重复调用holds_alternative -
std::get仅在 100% 确认当前是(v) T时使用(例如刚用holds_alternative检查过)(v) - 若需默认行为,把
std::monostate放在 variant 列表首位,它代表“空状态”,可被默认构造
std::variantv = 42; std::visit([](auto&& x) { using T = std::decay_t ; if constexpr (std::is_same_v ) { std::cout << "int: " << x; } else if constexpr (std::is_same_v ) { std::cout << "double: " << x; } else { std::cout << "string: " << x; } }, v);
为什么不能替代继承多态,以及它的代价在哪
std::variant 的类型集合在编译期固定,无法动态扩展;而继承多态靠运行时绑定,支持新子类无需修改已有容器逻辑。这是根本设计分歧,不是“哪个更好”,而是“是否适用”。
立即学习“C++免费学习笔记(深入)”;
空间上,std::variant 至少占用最大类型的大小(+ 少量索引开销),比指针大得多;时间上,std::visit 是零成本抽象(通常内联为跳转表或级联 if),但每次访问都要检查活跃类型——这在高频循环里可能成为瓶颈。
- 不要用
std::variant模拟多态:失去虚函数调度意义,且指针本身已可多态,variant 只是套壳 - 避免嵌套过深的 variant(如
variant),可读性和编译时间会显著恶化, variant<...>> - 若类型集合随业务增长频繁变更,说明它其实更适合用基类指针 + 工厂,而非 variant
常见误用:把 variant 当成万能容器
有人试图用 std::variant<:vector>, std::vector 来统一管理不同元素类型的容器——这违反了 variant 的设计意图,也丧失了 std::vector 的通用接口优势。
- 这种场景更适合模板参数化容器(如
template)struct MyContainer { std::vector data; }; - 或者用
std::any(运行时擦除,无类型安全)或void*(完全放弃安全)——但都不如 variant 适合其本职工作 - variant 的价值恰恰在于“有限、明确、静态可知的类型集合”,一旦开始绕开这个前提,就是在用错工具
真正难的不是写对语法,而是判断某个需求到底该用 std::variant、std::any、继承体系,还是干脆用模板——边界模糊时,先问一句:这些类型在编译期能不能全部列出来?如果不能,variant 就不该出场。









