`std::variant` 是 C++17 标准库引入的一个非常有用的模板类,定义在 `` 头文件中。 用一句话概括:**它是 C++ 中的“类型安全的联合体 (Type-safe Union)”。** ### 1. 核心概念 你可以把 `std::variant` 想象成一个盒子,这个盒子在任何时刻**只能**装着 A、B 或 C 类型中的**某一个**值。 * **对比 `struct`/`pair`**:结构体同时持有所有成员(A **和** B **和** C)。 * **对比 `union`**:传统的 C 语言 `union` 也可以只持有一个值,但它不知道当前存的是什么类型,容易出错(类型不安全)。 * **对比 `std::variant`**:它知道自己当前存的是什么类型,并且会自动管理内存(比如调用析构函数),非常安全。 ### 2. 基本用法 #### 定义和赋值 ```cpp #include #include #include int main() { // 定义一个 variant,它可以是 int, double, 或者 string std::variant v; v = 10; // 现在 v 存的是 int v = 3.14; // 现在 v 存的是 double v = "Hello World"; // 现在 v 存的是 string return 0; } ``` #### 获取值 (`std::get`) 你需要明确告诉编译器你想取什么类型的值。 ```cpp v = 100; // 正确:当前存的是 int int i = std::get(v); // 或者通过索引获取(0代表第一个类型 int) int i2 = std::get<0>(v); try { // 错误:当前存的是 int,但你试图取 string std::string s = std::get(v); } catch (const std::bad_variant_access& e) { std::cout << "类型取错了: " << e.what() << std::endl; } ``` #### 安全检查 (`std::holds_alternative` 和 `std::get_if`) 为了避免抛出异常,通常会先检查或使用指针方式获取。 ```cpp v = 3.14; // 方法1:先检查类型 if (std::holds_alternative(v)) { std::cout << "里面是 double: " << std::get(v) << std::endl; } // 方法2:使用 get_if (推荐,类似于 dynamic_cast) if (auto* p = std::get_if(&v)) { // 传入地址 std::cout << "里面是 double: " << *p << std::endl; } else { std::cout << "里面不是 double" << std::endl; } ``` ### 3. 高级用法:`std::visit` 这是 `std::variant` 最强大的地方。如果你不想写一堆 `if-else` 来判断当前是什么类型,可以使用 `std::visit` 来根据当前存储的类型自动调用对应的处理逻辑。 ```cpp #include #include int main() { std::variant v = "C++"; // 使用 lambda 表达式处理所有可能的情况 std::visit([](auto&& arg) { using T = std::decay_t; if constexpr (std::is_same_v) std::cout << "它是 int: " << arg << std::endl; else if constexpr (std::is_same_v) std::cout << "它是 double: " << arg << std::endl; else if constexpr (std::is_same_v) std::cout << "它是 string: " << arg << std::endl; }, v); } ``` *注:在实际工程中,通常会结合 `overloaded` 模式(一种辅助结构体)来为每种类型写单独的 lambda 函数,代码会更清晰。* ### 4. 为什么要用它?(应用场景) 1. **解析异构数据**:比如解析 JSON 或配置文件,一个字段可能是数字,也可能是字符串。 2. **错误处理 (Result Type)**: * 函数可能返回“成功的结果”或者“错误码”。 * `std::variant` 是非常优雅的实现方式。 3. **状态机**: * 如果一个对象有多个状态,且每个状态包含的数据不同。 * `std::variant` 可以完美表示当前状态。 4. **替代虚函数多态**: * 如果你有一组已知类型,使用 `std::variant` + `std::visit` 通常比继承 + 虚函数更快(没有虚表开销,内存更紧凑)。 ### 5. 总结 * **是什么**:C++17 的类型安全联合体。 * **能存啥**:定义时指定的一组类型中的某一个。 * **内存**:在栈上分配(通常大小等于最大成员的大小 + 一个索引标记),没有额外的堆内存分配(除非成员本身分配堆内存,如 `std::string`)。 * **优点**:类型安全、性能高、无需手动管理生命周期。