std::optional 是 C++ 17 引入的一个非常有用的容器,用来表示“可能存在也可能不存在”的值。它在处理可空值、错误返回、懒加载等场景中表现尤为出色。下面我们将从基本使用、常见坑、性能考虑以及高级用法几个方面来全面剖析 std::optional。
1. 基本使用
1.1 定义和初始化
#include <optional>
#include <string>
#include <iostream>
std::optional <int> findIndex(const std::string& s, char target) {
for (size_t i = 0; i < s.size(); ++i) {
if (s[i] == target) return static_cast <int>(i);
}
return std::nullopt; // 代表“未找到”
}
int main() {
auto idx = findIndex("hello", 'e');
if (idx) {
std::cout << "Index: " << *idx << '\n';
} else {
std::cout << "Character not found.\n";
}
}
- `std::optional ` 通过 `std::nullopt` 初始化为空状态。
- 通过
has_value()、operator bool()或直接if (opt)判断是否包含值。 - 访问值:
*opt或opt.value()。若为空,opt.value()会抛出std::bad_optional_access。
1.2 默认值
int value = idx.value_or(-1); // 若无值,返回 -1
value_or 可以一次性提供一个默认值,避免显式的 if 分支。
2. 常见坑与注意事项
| 位置 | 问题 | 解决方案 |
|---|---|---|
| 构造 | `std::optional | |
o(5);| 直接传入可空值;如果想传std::nullopt需显式std::nullopt` |
||
| 赋值 | o = std::nullopt; |
该语法可用,o 变为空状态 |
| 复制 | 复制 std::optional<std::string> 会复制字符串 |
若不想复制,可使用 std::optional<std::string_view> |
| 传参 | foo(opt) 需要考虑是否会拷贝 |
用 `const std::optional |
&传参,或移动语义std::move(opt)` |
3. 性能考量
- `std::optional ` 的大小等于 `sizeof(T)` + 1 字节(对齐后)。如果 `T` 很大,`optional` 仍然占据同样空间。
- 访问时不需要额外的
new/delete,与std::unique_ptr的动态分配不同。 - 需要时使用
std::optional<std::reference_wrapper<T>>或std::optional<T*>来避免复制。
4. 高级用法
4.1 与 std::variant 结合
using Result = std::variant<std::string, std::runtime_error>;
Result parse(const std::string& input) {
if (input.empty())
return std::runtime_error("empty input");
else
return input;
}
此时可以通过 `std::holds_alternative
` 判断错误与成功。 ### 4.2 作为 API 返回值 “`cpp std::optional> readFile(const std::string& path) { std::ifstream in(path); if (!in) return std::nullopt; // 读取… } “` 返回 `nullopt` 直接表示“文件不存在或读取失败”,不必抛异常。 ### 4.3 用于链式调用 “`cpp auto chain = [](std::optional a, std::optional b) { return a && b ? std::optional {a.value() + b.value()} : std::nullopt; }; “` 这样可以在多个可空值存在时才进行计算。 — ## 5. 与第三方库的协作 – **Boost.Optional**:C++17 标准化前,Boost 已经提供了类似功能。若项目仍使用 Boost,可无缝迁移。 – **std::expected**(C++23):类似 `optional` 的错误信息包装。将 `optional` 与 `expected` 结合可在 API 里分别传递“无结果”和“错误信息”。 — ## 6. 代码示例:实现一个简易配置解析器 “`cpp #include #include #include #include class Config { std::unordered_map data_; public: void set(const std::string& key, const std::string& val) { data_[key] = val; } std::optional get(const std::string& key) const { auto it = data_.find(key); if (it == data_.end()) return std::nullopt; return it->second; } // 读取整数 std::optional getInt(const std::string& key) const { if (auto val = get(key)) { try { return std::stoi(*val); } catch (…) { return std::nullopt; // 转换失败 } } return std::nullopt; } }; int main() { Config cfg; cfg.set(“port”, “8080”); cfg.set(“timeout”, “30”); if (auto port = cfg.getInt(“port”)) { std::cout