std::variant 通过类型安全访问、自动生命周期管理和强制穷尽的访问者模式,避免原始 union 的未定义行为、资源泄漏和手动类型判断错误。

std::variant 能防止未定义行为的读取
原始 union 允许你写入一个成员,却读取另一个——这直接触发未定义行为(UB)。比如写入 int 后读 double,编译器不拦、运行时也不报,结果完全不可预测。
而 std::variant 在读取前强制检查当前持有的类型:调用 std::get 时,若 v 实际不持有 T,会抛出 std::bad_variant_access 异常;用 std::get_if 则返回 nullptr,可安全判空。
构造/析构自动管理,避免资源泄漏
含非平凡类型(如 std::string、std::vector)的 union 无法自动调用构造函数和析构函数——你得手动管理,极易出错:
– 写入新值前忘了析构旧对象 → 内存泄漏或双重析构
– 读取前没调用构造 → 访问未初始化对象std::variant 完全接管生命周期:赋值、移动、析构时自动调用对应类型的构造/析构函数,无需用户干预。
支持访问者模式,避免手动类型判断
用 union 时,你必须自己维护一个额外的枚举字段(如 enum class tag { i, d, s };),并在每次读写前做 switch 判断——漏掉分支、类型与 tag 不同步、忘记更新 tag,全是隐患。std::visit 强制穷尽所有可能类型:
std::variantv = "hello"; std::visit([](const 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 if constexpr (std::is_same_v ) { std::cout << "string: " << x; } }, v);
如果未来给 variant 增加新类型但忘了更新 visit 分支,编译器直接报错,而不是静默运行到崩溃。
立即学习“C++免费学习笔记(深入)”;
std::holds_alternative 和 valueless_by_exception 的显式状态控制
std::variant 提供 std::holds_alternative 这种类型安全的查询接口,比手写 if (tag == tag::s) ... 更可靠,且与类型系统联动(改类型名或顺序不影响逻辑)。
更重要的是,它明确支持 valueless_by_exception 状态:当某次变体赋值因异常中途失败(如 std::string 构造抛异常),v.valueless_by_exception() 返回 true,程序可据此拒绝后续访问——而原始 union 在这种情况下根本无法表达“无效”状态,只能靠约定或额外字段,极难保证一致性。










