std::variant 是 C++17 标准中引入的联合体类型,能够在运行时安全地保存多种类型中的一种。与传统的 union 不同,std::variant 对类型安全、内存管理、拷贝构造/移动构造等做了完整封装,使得多态性在类型层面更为可控。
1. 基本语法
#include <variant>
#include <string>
#include <iostream>
using Variant = std::variant<int, double, std::string>;
int main() {
Variant v = 42; // 保存 int
v = std::string("hello"); // 现在保存 std::string
std::cout << std::get<std::string>(v) << '\n';
}
- 初始化:可以直接用括号或赋值语句。
- 访问:`std::get (v)` 访问存储的值,如果类型不匹配会抛出 `std::bad_variant_access`。
2. 访问方式
| 方式 | 说明 |
|---|---|
| `std::get | |
| ` | 直接访问指定类型 |
| `std::get_if | |
| 返回指针,若不匹配返回nullptr` |
|
std::visit |
访问器函数,支持多态访问 |
std::visit 示例
#include <variant>
#include <iostream>
#include <string>
struct Visitor {
void operator()(int i) const { std::cout << "int: " << i << '\n'; }
void operator()(double d) const { std::cout << "double: " << d << '\n'; }
void operator()(const std::string& s) const { std::cout << "string: " << s << '\n'; }
};
int main() {
std::variant<int, double, std::string> v = 3.14;
std::visit(Visitor{}, v);
}
std::visit 将会调用与当前存储类型匹配的 operator(),实现类似多重重载的效果。
3. 关联索引
std::variant 还提供了内部索引,表明当前存储的类型在模板参数列表中的位置。
std::cout << v.index() << '\n'; // 输出 1(double 在位置 1)
可使用 std::variant_npos 判断是否为空。
4. 空 variant
std::variant 可以是空的(没有值),此时使用 std::monostate 或默认构造。访问空 variant 会抛异常。
std::variant<int, std::string> v;
try {
std::get <int>(v); // 抛异常
} catch (const std::bad_variant_access& e) {
std::cout << "Variant is empty\n";
}
5. 典型应用场景
-
函数返回多种可能类型
std::variant<int, std::string> parse(const std::string& s) { if (std::isdigit(s[0])) return std::stoi(s); else return s; } -
实现简单的状态机
每个状态用一个独立类型表示,状态切换通过std::variant存储。 -
事件系统
不同事件类型用不同结构体表示,统一用std::variant传递。
6. 性能考虑
- 内存占用:
std::variant的大小等于其最大成员的大小加上索引所需空间(通常是size_t)。 - 对齐:内部对齐确保所有成员均可安全存储。
- 构造/析构:只会调用当前类型的构造/析构,避免无用操作。
7. 与 std::any 的区别
| 特性 | std::variant | std::any |
|---|---|---|
| 类型安全 | 只允许预先声明的类型 | 任意类型 |
| 访问 | 需要指定类型 | 需要使用 `any_cast |
| ` | ||
| 性能 | 更小更快 | 更大更慢 |
| 用途 | 类型已知但不确定 | 类型未知 |
8. 进阶:自定义访问器
如果想在访问时做类型映射或处理,可以通过 std::visit 搭配 lambda 表达式:
auto result = std::visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, int>) return arg * 2;
else if constexpr (std::is_same_v<T, std::string>) return arg + "!";
else return arg;
}, v);
这段代码对不同类型执行不同逻辑,返回值根据 lambda 结果自动推断。
9. 兼容 C++20/23 的改进
std::variant在 C++20 引入了std::variant_alternative的std::variant_alternative_t。- C++23 对
std::visit进行了一些性能优化,内部实现采用了更快的分支预测。
10. 小结
std::variant 是一种安全、灵活且高效的多类型容器,适用于需要在运行时选择不同类型但类型已知的场景。掌握其基本用法、访问方式以及与 std::visit 的配合,将大大提升 C++ 代码的可维护性与可读性。