说明

std::optional 是一个类模板,其管理一个可选的包含值,也就是说值可以存在或者不存在。optional 的一个常见用例是可能失败的函数的返回值。与其他方法(例如 std::pair<T,bool>)相反, optional 可以很好地处理构建成本高的对象并且更具可读性,因为意图已明确表达。

optional<T> 的任何实例在某个时间点要么包含值,要么不包含值。如果 optional<T> 包含一个值,该值保证能够作为 optional<T> 的一部分进行分配,即永远不会发生动态内存分配。因此,即使定义了 operator*()operator->(),可选对象也是创建对象,而不是指针。

当一个 optional<T> 类型的对象在上下文中转换为 bool 时,如果该对象包含一个值,则转换返回 true,如果它不包含一个值,则返回 false

对象在下列情况下包含值:

  • 对象是用 T 类型的值或另一个包含值的 optional 对象进行初始化/分配。

对象在下列情况下不包含值:

  • 对象是默认初始化的
  • 对象是从 std::nullopt_t 或者不包含值的 optional 对象进行初始化/分配
  • 成员函数 reset() 被调用

没有 optional 的引用。如果程序使用引用类型实例化 optional,则该程序格式错误。

实例

#include <iostream>
#include <optional>
#include <string>

std::optional<std::string> getName(bool b) {
if (b) {
return "Tom";
} else {
return std::nullopt;
}
}

// value() 函数会抛异常,因此在使用时最好放在 try ... catch ... 中,或者在获取前判断一下
void test_value() {
// 抛异常的情况
try {
auto name = getName(false).value();
} catch(const std::bad_optional_access& e) {
std::cout << "test_value 0. throw: " << e.what() << '\n';
}

// 不抛异常的情况
try {
auto name = getName(true).value();
std::cout << "test_value 1. name: " << name << '\n';
} catch(const std::bad_optional_access& e) {
std::cout << "test_value 1. throw: "<< e.what() << '\n';
}

// 取值前进行判断,隐式 bool 运算转换
auto nameOpt = getName(false);
if (nameOpt) {
std::cout << "test_value 2. name: " << nameOpt.value() << '\n';
} else {
std::cout << "test_value 2. value not set\n";
}

nameOpt = getName(true);
if (nameOpt) {
std::cout << "test_value 3. name: " << nameOpt.value() << '\n';
} else {
std::cout << "test_value 3. value not set\n";
}

// 取值前进行判断,has_value 进行判断
nameOpt = getName(false);
if (nameOpt.has_value()) {
std::cout << "test_value 4. name: " << nameOpt.value() << '\n';
} else {
std::cout << "test_value 4. value not set\n";
}

nameOpt = getName(true);
if (nameOpt.has_value()) {
std::cout << "test_value 5. name: " << nameOpt.value() << '\n';
} else {
std::cout << "test_value 5. value not set\n";
}
}

// value_or 如果检测到未设置值,则会返回一个默认值
void test_value_or()
{
auto name = getName(false).value_or("none");
std::cout << "test_value_or 0. name: " << name << '\n';

name = getName(true).value_or("none");
std::cout << "test_value_or 1. name: " << name << '\n';
}

int main()
{
test_value();
test_value_or();
}

程序输出:

name is empty
test_value 0. throw: Bad optional access
test_value 1. name: Tom
test_value 2. value not set
test_value 3. name: Tom
test_value 4. value not set
test_value 5. name: Tom
test_value_or 0. name: none
test_value_or 1. name: Tom