在 C++17 标准中加入了 std::optional,它提供了一种类型安全的方式来表示“可能存在也可能不存在”的值。相比于裸指针或 sentinel 值,std::optional 让代码更直观、更易维护。本文将从基础语法、常见使用场景、与容器的配合,到高级技巧(如自定义缺失值、移动语义优化、与 std::variant 的互补)进行系统讲解。
1. 基本定义与构造
#include <optional>
#include <iostream>
#include <string>
std::optional <int> findEven(const std::vector<int>& nums) {
for (int n : nums) {
if (n % 2 == 0) return n; // 返回值会自动包装进 std::optional
}
return std::nullopt; // 表示“无结果”
}
- `std::optional ` 本质是一个“容器”,内部包含一个 `T` 的实例以及一个布尔标志 `has_value_`。
std::nullopt是一个特殊的常量,用来构造一个空状态的std::optional。
2. 访问值
auto opt = findEven({1, 3, 5, 7, 8});
if (opt) { // 同 `opt.has_value()`
std::cout << "Found: " << *opt << '\n'; // `operator*`
std::cout << "Direct: " << opt.value() << '\n'; // `value()`
} else {
std::cout << "No even number\n";
}
operator bool让std::optional在布尔上下文中自然判定是否有值。operator*与value()两者功能相同,区别是value()会在空状态下抛出bad_optional_access。
3. 默认值与 value_or
int result = opt.value_or(0); // 如果 opt 为空,则返回 0
此方法在需要“兜底”值时非常方便,避免显式的 if-else。
4. 与容器的配合
4.1 std::vector 的 find_if
auto opt = std::find_if(nums.begin(), nums.end(),
[](int n){ return n % 2 == 0; });
if (opt != nums.end()) {
std::cout << "First even: " << *opt << '\n';
}
在标准算法中,find_if 的返回值为迭代器;若想统一使用 std::optional,可写一个适配器:
template<typename It>
std::optional<std::remove_reference_t<decltype(*It{})>> optionalFind(It first, It last, auto pred) {
auto it = std::find_if(first, last, pred);
if (it != last) return *it;
return std::nullopt;
}
4.2 std::map 的 find
auto it = map.find(key);
if (it != map.end()) {
std::cout << "Found: " << it->second << '\n';
}
同样可以使用适配器,将 std::map 的 find 转为 `std::optional