文章目录
-
-
- [1. **概述**](#1. 概述)
- [2. **成员函数**](#2. 成员函数)
-
- [2.1 **构造函数和析构函数**](#2.1 构造函数和析构函数)
- [2.2 **赋值操作符**](#2.2 赋值操作符)
- [2.3 **观察者函数**](#2.3 观察者函数)
- [2.4 **访问函数**](#2.4 访问函数)
- [2.5 **单子操作(自 C++26 起)**](#2.5 单子操作(自 C++26 起))
- [3. **非成员函数**](#3. 非成员函数)
- [4. **辅助类和常量**](#4. 辅助类和常量)
- [5. **示例代码**](#5. 示例代码)
- [6. **总结**](#6. 总结)
-
std::variant
是 C++17 引入的一个类模板,用于表示类型安全的联合体(union)。与传统的 C 风格联合体不同, std::variant
提供了更强的类型安全性和更丰富的功能。它可以在任何给定时间包含多个备用类型之一的值,并且可以处理这些类型的转换和访问。
1. 概述
std::variant
的定义如下:
cpp
template<class... Types>
class variant;
Types...
:一个或多个类型,表示variant
可以存储的备用类型。所有类型都必须满足可销毁的要求,不能是数组、引用或void
类型。
std::variant
在任何给定时间点要么包含其备用类型之一的值,要么在错误的情况下不包含任何值(这种情况很少见,通常发生在异常抛出时)。std::variant
不会分配额外的动态内存,所有对象都嵌套在 variant
对象本身中。
2. 成员函数
2.1 构造函数和析构函数
- 默认构造函数 :如果第一个备用类型是可默认构造的,则创建一个包含该类型的
variant
;否则,variant
不可默认构造。 - 带参数的构造函数 :接受一个备用类型的值,初始化
variant
以包含该值。 in_place
构造 :就地构造variant
中的值,避免不必要的拷贝或移动。- 析构函数 :销毁
variant
对象及其包含的值。
2.2 赋值操作符
operator=
:将一个variant
的内容赋值给另一个variant
,或者将一个备用类型的值赋值给variant
。
2.3 观察者函数
index()
:返回variant
当前所包含的备用类型的零基索引。如果variant
处于无效状态(即valueless_by_exception
),则返回variant_npos
。valueless_by_exception()
:检查variant
是否处于无效状态,即由于异常抛出而不包含任何值。
2.4 访问函数
std::get<T>(variant)
或std::get<I>(variant)
:根据类型T
或索引I
获取variant
中的值。如果variant
不包含该类型或索引的值,抛出std::bad_variant_access
异常。std::get_if<T>(&variant)
或std::get_if<I>(&variant)
:获取指向variant
中值的指针,根据类型T
或索引I
。如果variant
不包含该类型或索引的值,返回nullptr
。holds_alternative<T>(variant)
:检查variant
是否当前包含给定类型T
。
2.5 单子操作(自 C++26 起)
visit
:使用variant
所包含的参数调用提供的函数对象。visit
是std::variant
的成员函数,允许直接对variant
进行访问。
3. 非成员函数
std::visit
:使用一个或多个variant
所包含的参数调用提供的函数对象。std::visit
是一个非常强大的工具,用于处理variant
中的不同类型。std::swap
:专门化std::swap
算法,用于交换两个variant
的内容。- 比较运算符 :
std::variant
支持按字典顺序进行比较。C++20 引入了三路比较运算符operator<=>
,它可以在支持三路比较的类型上使用。
4. 辅助类和常量
std::monostate
:占位符类型,用作不可默认构造的类型的variant
中的第一个备选方案。std::monostate
允许variant
可以默认构造。std::bad_variant_access
:当尝试访问variant
中不存在的值时抛出的异常。std::variant_size
和std::variant_size_v
:在编译时获取variant
的备选方案列表的大小。std::variant_alternative
和std::variant_alternative_t
:在编译时获取由索引指定的备选方案的类型。std::hash<std::variant>
:对std::variant
的哈希支持。std::variant_npos
:variant
在无效状态下的索引。
5. 示例代码
示例 1:基本用法
cpp
#include <iostream>
#include <variant>
#include <string>
int main() {
// 定义一个 variant,可以存储 int 或 float
std::variant<int, float> v;
// 初始化为 int
v = 42;
std::cout << "v contains: " << std::get<int>(v) << '\n';
// 修改为 float
v = 3.14f;
std::cout << "v contains: " << std::get<float>(v) << '\n';
// 尝试访问不存在的类型,会抛出异常
try {
std::get<std::string>(v);
} catch (const std::bad_variant_access& e) {
std::cout << "Error: " << e.what() << '\n';
}
return 0;
}
输出:
v contains: 42
v contains: 3.14
Error: std::bad_variant_access: Attempted to access an inactive value in a variant.
示例 2:使用 std::visit
访问 variant
cpp
#include <iostream>
#include <variant>
#include <string>
// 定义一个 visitor 结构体,用于处理不同的类型
struct PrintVisitor {
void operator()(int i) const {
std::cout << "Integer: " << i << '\n';
}
void operator()(float f) const {
std::cout << "Float: " << f << '\n';
}
void operator()(const std::string& s) const {
std::cout << "String: " << s << '\n';
}
};
int main() {
// 定义一个 variant,可以存储 int、float 或 string
std::variant<int, float, std::string> v;
// 初始化为 int
v = 42;
std::visit(PrintVisitor{}, v);
// 修改为 float
v = 3.14f;
std::visit(PrintVisitor{}, v);
// 修改为 string
v = "Hello, World!";
std::visit(PrintVisitor{}, v);
return 0;
}
输出:
Integer: 42
Float: 3.14
String: Hello, World!
示例 3:使用 std::monostate
作为默认值
cpp
#include <iostream>
#include <variant>
#include <string>
int main() {
// 定义一个 variant,可以存储 int、float 或 monostate
std::variant<int, float, std::monostate> v;
// 默认构造,包含 monostate
std::cout << "v contains: " << v.index() << '\n'; // 输出 2,因为 monostate 是第三个类型
// 修改为 int
v = 42;
std::cout << "v contains: " << std::get<int>(v) << '\n';
// 修改为 float
v = 3.14f;
std::cout << "v contains: " << std::get<float>(v) << '\n';
// 修改回 monostate
v = std::monostate{};
std::cout << "v contains: " << v.index() << '\n'; // 输出 2
return 0;
}
输出:
v contains: 2
v contains: 42
v contains: 3.14
v contains: 2
示例 4:使用 std::get_if
检查并访问值
cpp
#include <iostream>
#include <variant>
#include <string>
int main() {
// 定义一个 variant,可以存储 int 或 float
std::variant<int, float> v;
// 初始化为 int
v = 42;
// 使用 get_if 检查并访问 int
if (auto* p = std::get_if<int>(&v)) {
std::cout << "v contains int: " << *p << '\n';
} else {
std::cout << "v does not contain int\n";
}
// 修改为 float
v = 3.14f;
// 使用 get_if 检查并访问 float
if (auto* p = std::get_if<float>(&v)) {
std::cout << "v contains float: " << *p << '\n';
} else {
std::cout << "v does not contain float\n";
}
return 0;
}
输出:
v contains int: 42
v contains float: 3.14
6. 总结
std::variant
是 C++17 引入的一个非常有用的工具,特别适用于需要在多个不同类型之间切换的场景。它提供了类型安全的联合体功能,并且通过 std::visit
和 std::get
等函数,使得访问和操作 variant
中的值变得非常方便。
std::variant
的主要优点包括:
- 类型安全:避免了传统 C 风格联合体中的类型混淆问题。
- 灵活性:可以存储多个不同类型,并且可以处理这些类型的转换和访问。
- 性能高效 :所有对象都嵌套在
variant
对象本身中,不会分配额外的动态内存。 - 丰富的功能 :提供了
std::visit
、std::get
、std::get_if
等强大工具,使得代码更加简洁和易读。
如果你有更多具体的问题或需要进一步的帮助,请随时提问!