
union 在 C++ 里到底能不能直接存 string 或 vector
不能。原生 union 只允许含 trivially copyable 类型,std::string 和 std::vector 都有非平凡构造函数、析构函数和拷贝控制,放进去就是未定义行为——编译可能通过,但运行时大概率崩溃或内存泄漏。
常见错误现象:union { std::string s; int i; } u; 编译报错(C++11 起)或静默编译成功但调用 u.s = "hello" 后访问 u.i 崩溃。
- 必须手动管理生命周期:用
placement new构造、显式调用析构函数 - 类型切换时极易漏掉析构旧成员,导致资源泄漏
- 没有类型标签,读取前必须靠外部状态判断当前存的是什么——容易误读
std::variant 替代 union 的最小可行写法
std::variant 是类型安全的“带标签联合体”,它自动管理各分支类型的构造/析构,并提供 std::visit 安全分发。最简可用写法就三步:
- 声明:如
std::variant<int std::string double> v;</int> - 赋值:直接
v = 42;或v = std::string("ok");,会自动销毁旧值并构造新值 - 读取:必须用
std::visit或std::get_if,不能裸访问 —— 这是强制的安全检查
示例:
std::variant<int, std::string> v = "hello";
std::visit([](auto&& x) { std::cout << x << "\n"; }, v); // 输出 hello
std::get(v) 和 std::get_if(&v) 的区别在哪
前者要求 v 当前**一定**持有 T 类型,否则抛 std::bad_variant_access;后者是安全探测,返回 T*,空指针表示当前不是该类型。
立即学习“C++免费学习笔记(深入)”;
- 用
std::get<int>(v)</int>前,必须已知类型(比如刚赋过值),否则就是运行时风险 -
std::get_if<:string>(&v)</:string>更适合条件分支逻辑,比如:“如果是 string 就转小写,否则跳过” - 性能上,
std::get_if多一次 tag 比较,但几乎可忽略;关键是语义清晰,不抛异常
为什么 std::variant 不是万能替代,哪些场景还得手写 union
std::variant 有运行时空间开销(至少一个字节存 type index)、构造/析构开销、且不支持引用/数组/不完整类型作为备选项。某些硬实时或嵌入式场景仍需原始 union。
- 需要零开销抽象(比如硬件寄存器映射):只能用
union+std::bit_cast(C++20)或 memcpy - 想共享同一块内存给多个 POD 结构(如网络包头解析):
union更直接 - 类型列表超长(>64 个)或含模板参数极复杂的类型:部分编译器对
std::variant实现有深度限制
真正麻烦的从来不是语法怎么写,而是你得时刻清楚:这个变量当前在内存里到底是什么,谁负责它的生命期,以及别人读它的时候有没有同步好状态。









