variant
std::variant 是 C++17 引入的类型安全的联合体(union),可以在编译期确定的多个类型中同时只持有其中一个值。它取代了 C 风格 union(后者不能持有非平凡类型如 std::string),也避免了手动 void* + 类型标签的不安全模式。
头文件与基本特征
#include <variant>
| 特征 | 说明 |
|---|---|
| 作用 | 类型安全的 union,某一时刻持有指定类型之一 |
| 类型检查 | 编译期确定可选类型列表 |
| 访问方式 | get<T>() 或 visit() |
| 空状态 | valueless_by_exception 仅在异常构造失败时出现 |
| 存储 | 分配足够容纳最大类型的栈内存 |
基本用法
#include <iostream>
#include <string>
#include <variant>
int main() {
std::variant<int, double, std::string> v;
v = 42;
std::cout << std::get<int>(v) << '\n'; // 42
v = std::string("hello");
std::cout << std::get<std::string>(v) << '\n'; // hello
return 0;
}
visit 访问模式
std::visit 是处理 variant 最安全的方式——编译期保证覆盖所有类型:
#include <iostream>
#include <string>
#include <variant>
struct Printer {
void operator()(int x) const { std::cout << "int: " << x << '\n'; }
void operator()(double x) const { std::cout << "double: " << x << '\n'; }
void operator()(const std::string& x) const { std::cout << "string: " << x << '\n'; }
};
int main() {
std::variant<int, double, std::string> v = 3.14;
std::visit(Printer{}, v); // double: 3.14
return 0;
}
常用接口
| 接口 | 作用 |
|---|---|
get<T>() | 获取指定类型的值,类型错误抛 bad_variant_access |
get_if<T>(&v) | 返回指针,类型不匹配返回 nullptr |
index() | 当前持有类型的索引(0-based) |
visit(visitor, v) | 用 visitor 处理 variant |
emplace<T>(args...) | 原地构造新值 |
holds_alternative<T>() | 检查当前是否持有某类型 |
典型场景
- 解析结果:可能是值、错误码、或错误消息。
- 状态机:每个状态是不同类型,转换时切换。
- JSON 值:number / string / bool / null 的统一表示。
使用注意
- 优先用
visit而不是get,避免遗漏类型处理。 - 不要用 variant 装"50 种类型",那通常是设计问题。
- 和
optional区别:optional<T>是"T 或空",variant<T1, T2, ...>是"类型之一"。
小结
std::variant 给了 C++ 一个类型安全的 union,配合 std::visit 可实现编译期强制覆盖所有情况的模式匹配。它比裸 union 安全得多,也是很多设计模式(如结果、状态)的更好载体。