C++17 和 C++20 中的新容器与工具:std::optional、std::variant 和 std::span
C++17 和 C++20 引入了三个重要的容器与工具:std::optional
、std::variant
和 std::span
,它们分别解决了值可能缺失、类型安全的联合以及视图抽象的问题。以下是对这三个特性的详细介绍:
一、std::optional
:可能缺失的值
1. 基本概念
std::optional
表示一个可能存在或不存在的值,替代了使用空指针或特殊值(如 -1
)表示缺失的传统方法。
2. 核心接口
#include <optional>// 创建optional
std::optional<int> maybe_value; // 默认空
std::optional<int> opt1 = 42; // 有值
std::optional<int> opt2 = std::nullopt; // 空// 查询状态
bool has_value = opt1.has_value(); // true
bool is_empty = !opt1; // false// 获取值
int value = opt1.value(); // 若为空则抛出异常
int value_or = opt1.value_or(0); // 若为空则返回默认值
int direct = *opt1; // 解引用(不检查空值)
3. 应用场景
-
函数返回值:替代返回指针或错误码
std::optional<User> find_user(std::string_view name) {if (exists(name)) {return get_user(name);}return std::nullopt; }
-
可能缺失的配置参数
struct Config {std::optional<int> timeout; // 可选超时时间 };
二、std::variant
:类型安全的联合
1. 基本概念
std::variant
表示一个可变类型,它可以持有多种类型中的某一种,但同一时间只能是其中一种类型。
2. 核心接口
#include <variant>// 定义variant
std::variant<int, std::string, double> var; // 默认第一个类型(int)// 赋值
var = 42; // 持有int
var = "hello"; // 持有std::string
var = 3.14; // 持有double// 查询当前类型
size_t index = var.index(); // 返回类型索引// 获取值
if (auto* p = std::get_if<int>(&var)) {// var 持有int
} else if (auto* p = std::get_if<std::string>(&var)) {// var 持有std::string
}// 访问(类型安全)
std::visit([](auto& value) {std::cout << "Value: " << value << '\n';
}, var);
3. 应用场景
-
异构数据结构
using JsonValue = std::variant<std::nullptr_t, bool, int, double, std::string, std::vector<JsonValue>, std::map<std::string, JsonValue>>;
-
替代错误码
using Result = std::variant<Data, ErrorCode>;Result process() {if (success) return data;else return ErrorCode::Failure; }
三、std::span
:轻量级视图
1. 基本概念
std::span
表示一个连续内存区域的视图,它不拥有内存,类似于 std::string_view
对字符串的处理方式。
2. 核心接口
#include <span>// 创建span
std::vector<int> vec = {1, 2, 3, 4};
std::span<int> sp1(vec); // 从vector创建
std::span<int> sp2(&vec[0], 3); // 指定起始地址和长度
int arr[] = {5, 6, 7};
std::span<int> sp3(arr); // 从数组创建// 访问元素
int first = sp1[0]; // 下标访问
size_t size = sp1.size(); // 元素数量
bool empty = sp1.empty(); // 是否为空// 子视图
std::span<int> sub = sp1.subspan(1, 2); // 从位置1开始的2个元素
3. 应用场景
-
函数参数:替代数组指针和长度的分离传递
void process_data(std::span<const int> data) {// 处理数据,无需关心具体容器类型 }process_data(vec); // 传递vector process_data(arr); // 传递数组
-
切片操作
std::span<int> head = sp1.first(2); // 前两个元素 std::span<int> tail = sp1.last(2); // 后两个元素
四、三者对比与配合使用
特性 | std::optional | std::variant | std::span |
---|---|---|---|
用途 | 可能缺失的值 | 类型安全的联合 | 连续内存的视图 |
内存管理 | 包含值或为空 | 包含某一种类型的值 | 不拥有内存,指向现有数据 |
典型场景 | 函数返回值可能缺失 | 异构数据结构 | 泛型数据处理 |
空状态 | 有(std::nullopt ) | 无(必须包含一种类型) | 无(但可能为空视图) |
组合使用示例:
// 返回可选的span(可能为空)
std::optional<std::span<const int>> get_data() {if (has_data()) {return std::span(data_buffer);}return std::nullopt;
}// 处理可能是不同类型的数据源
using DataSource = std::variant<std::vector<int>, std::array<int, 10>>;void process(DataSource source) {std::visit([](auto& data) {std::span<const int> view(data);// 统一处理数据}, source);
}
五、性能与注意事项
1. 性能考量
std::optional
:通常为值大小 + 1字节标记(可能优化为零开销)std::variant
:大小为最大类型 + 类型标记(通常为size_t
)std::span
:零开销抽象(仅包含指针和长度)
2. 注意事项
std::optional
:避免过度使用,优先使用值语义std::variant
:访问时需确保类型匹配(使用std::get_if
或std::visit
)std::span
:确保引用的内存有效(避免悬空span)
六、示例:综合应用
#include <iostream>
#include <optional>
#include <variant>
#include <span>
#include <vector>// 可能返回空的字符串处理函数
std::optional<std::string> format_data(std::span<const int> data) {if (data.empty()) return std::nullopt;std::string result;for (int value : data) {result += std::to_string(value) + " ";}return result;
}// 处理不同类型的数据源
using DataSource = std::variant<std::vector<int>, std::array<int, 5>>;std::optional<std::string> process(DataSource source) {return std::visit([](auto& data) -> std::optional<std::string> {std::span<const int> view(data);return format_data(view);}, source);
}int main() {std::vector<int> vec = {1, 2, 3};std::array<int, 5> arr = {4, 5, 6, 7, 8};if (auto result = process(vec); result.has_value()) {std::cout << "Vec: " << *result << '\n';}if (auto result = process(arr); result.has_value()) {std::cout << "Array: " << *result << '\n';}
}
std::optional
、std::variant
和 std::span
是现代 C++ 中强大的工具,它们分别解决了值缺失、类型安全和内存视图的问题,使代码更安全、更简洁。合理使用这些特性可以减少错误,提高性能,并增强代码的表达力。