std optional 的使用
std optional 的使用
文章目录
- std optional 的使用
- 1. `std::optional` 基本概念
- 2. `std::optional` 的基本用法
- 定义和初始化
- 使用 `std::optional` 的成员函数
- 3. `std::optional` 常用操作
- 4. 使用 `std::optional` 的注意事项
- 总结
std::optional
是 C++17 引入的一个标准库模板类,它提供了一种方式来表示一个值可能存在也可能不存在(即它的值是“可选的”)。它可以用于表示函数返回值可能为空的情况,避免直接使用指针或特殊值(如 nullptr
或 -1
)来表示缺失的值。
1. std::optional
基本概念
std::optional<T>
是一个模板类,其中 T
是存储类型。当 std::optional
对象有值时,您可以像普通对象一样访问它的值;如果它没有值,则它处于“无效”状态。
它提供了两种状态:
- 有值:表示该
optional
存储了一个类型为T
的值。 - 无值:表示该
optional
没有值,可以认为它是“空的”或“未定义的”。
2. std::optional
的基本用法
定义和初始化
#include <iostream>
#include <optional>
std::optional<int> get_value(bool should_return_value) {
if (should_return_value) {
return 42; // 有值
} else {
return std::nullopt; // 没有值
}
}
int main() {
auto val1 = get_value(true); // 有值
auto val2 = get_value(false); // 没有值
if (val1) {
std::cout << "val1: " << *val1 << std::endl; // 解引用获取值
} else {
std::cout << "val1 is empty" << std::endl;
}
if (val2) {
std::cout << "val2: " << *val2 << std::endl;
} else {
std::cout << "val2 is empty" << std::endl;
}
return 0;
}
在这个例子中,get_value
函数根据 should_return_value
参数的值来返回一个 std::optional<int>
。如果返回值存在,就通过 return 42
返回一个有值的 optional
;如果返回值不存在,使用 std::nullopt
表示返回一个没有值的 optional
。
使用 std::optional
的成员函数
std::optional
提供了许多成员函数用于操作 optional
对象:
has_value()
:检查optional
是否有值。返回true
表示有值,false
表示没有值。value()
:如果optional
有值,返回存储的值;否则抛出std::bad_optional_access
异常。value_or(T&& default_value)
:如果optional
有值,返回存储的值;否则返回提供的默认值。operator*
和operator->
:可以解引用optional
以访问存储的值。
std::optional<int> maybe_value = 10;
if (maybe_value.has_value()) {
std::cout << "Value: " << maybe_value.value() << std::endl;
} else {
std::cout << "No value!" << std::endl;
}
std::optional<int> empty_value;
std::cout << "Value or default: " << empty_value.value_or(42) << std::endl; // 输出 42
3. std::optional
常用操作
- 初始化:
std::optional<T> opt;
// 默认构造,表示没有值std::optional<T> opt = T(10);
// 使用值初始化,表示有值std::optional<T> opt = std::nullopt;
// 无值
- 检查是否有值:
if (opt) { // 或者 if (opt.has_value())
std::cout << *opt << std::endl;
}
- 解引用和访问值:
if (opt) {
std::cout << *opt << std::endl; // 解引用
}
- 提供默认值:
int value = opt.value_or(100); // 如果没有值,返回 100
- 重置值:
opt.reset(); // 使 opt 变为没有值的状态
4. 使用 std::optional
的注意事项
- 访问值时要小心:
std::optional
可能没有值,直接访问没有值的optional
(例如,使用opt
或opt.value()
)会导致未定义行为或抛出异常。因此,通常需要先检查optional
是否有值。
if (opt) {
std::cout << *opt << std::endl;
} else {
std::cout << "opt does not have a value" << std::endl;
}
- 使用默认值: 使用
value_or
可以提供一个默认值,避免异常抛出或访问无值的情况。
int value = opt.value_or(42); // 如果没有值,则返回 42
- 性能考虑: 使用
std::optional
会有一些额外的内存开销,因为它需要在内部存储一个标志来指示是否有值。对于基本类型,optional
会比直接使用值类型稍微慢一些。对于大型对象或者需要多次检查值的场景,可以考虑优化设计,减少不必要的optional
使用。 - 与指针的比较:
std::optional<T>
与指针(如std::unique_ptr<T>
或裸指针)有所不同。optional
只是表示一个值的“缺席”,而指针可能指向nullptr
或指向一个对象。在设计中,根据需要选择是否使用optional
或指针。 - 不能用作容器的成员: 由于
std::optional
本身是一个包含类型的容器,不能将它作为容器成员来存储其他对象(例如std::vector<std::optional<T>>
),否则会增加额外的内存开销和复杂度。可以考虑其他方式来存储可选类型。
总结
std::optional
是 C++17 中非常有用的工具,能够优雅地处理“可能无值”的情况。它通过明确的 API 提供了检查值存在与否、访问值、提供默认值等功能,减少了使用裸指针或特殊值来表示缺失值的错误。使用时要注意检查 optional
是否有值,避免访问无值的状态导致的异常或错误。