文章

Optional

Optional

[TOC]

std::optional

在C++17标准中引入的std::optional是一个模板类,用于表示某个对象的值是可选的,即它可能包含一个值也可能不包含值std::optional是对在编程中常见的可能不返回值的情况的一种类型安全的替代。

std::optional<T>可以看作一个容器,它最多包含一个类型为T的对象。std::optional主要解决的是某些函数可能不返回结果,或者某些对象在某个时间点可能不持有有效值的问题。

基本用法

使用std::optional非常简单,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <optional>

std::optional<int> GetNumber(bool shouldReturnNumber) {
    if (shouldReturnNumber) {
        return 42; // 返回一个包含int值的optional
    }
    return {}; // 返回一个不包含值的optional
}

int main() {
    auto number = GetNumber(true);
    if (number) { // 检查optional是否包含值
        std::cout << "Number is: " << *number << std::endl; // 访问optional中的值
    }
    
    auto noNumber = GetNumber(false);
    std::cout << "No number value: " << noNumber.value_or(0) << std::endl; // 提供默认值

    return 0;
}

使用场景

std::optional的使用场景包括但不限于:

  • 函数可能失败,并且没有明显的错误值时(例如,从数据库中查找记录可能找到也可能找不到)。
  • 函数可能不返回值,当不想使用异常或者全局变量来报告错误状态。
  • 类成员可以在某些时候没有值,std::optional提供了一种表示和检查这种状态的方法。

使用std::optional比使用指针更安全,因为指针可能为空,这需要手动检查。而std::optional能够明确表示值的存在或缺失,并配备了相应的方法,使代码的意图更明确,也更容易维护。此外,std::optional还有助于避免使用魔法数(比如用-1表示错误或缺失值)来表示缺失值,这样可以减少潜在的bug和误解。

基本函数

  1. 构造函数: std::optional 可以通过其构造函数来构造,既可以构造出一个空的 optional,也可以构造出包含值的 optional

    1
    2
    3
    
    std::optional<int> opt1; // 默认构造一个空的optional
    std::optional<int> opt2 = std::nullopt; // 显式构造一个空的optional
    std::optional<int> opt3 = 5; // 构造一个包含整数5的optional
    
  2. operator-> 和 operator*: 提供对存储值的访问,如果 std::optional 包含一个值。

    1
    2
    3
    
    std::optional<std::string> opt("hello");
    std::cout << *opt << std::endl; // 使用operator*来解引用
    std::cout << opt->size() << std::endl; // 使用operator->访问string的成员函数
    
  3. has_value(): 检查 std::optional 是否包含一个值。

    1
    2
    3
    
    if (opt.has_value()) { // 或使用 if (opt)
        std::cout << "opt has a value" << std::endl;
    }
    
  4. value(): 访问 std::optional 中的值,如果 std::optional 为空,则抛出 std::bad_optional_access 异常。

    1
    2
    3
    4
    5
    
    try {
        std::cout << opt.value() << std::endl;
    } catch (const std::bad_optional_access& e) {
        std::cout << e.what() << std::endl;
    }
    
  5. value_or(): 访问 std::optional 中的值,如果 std::optional 为空,则返回一个默认值。

    1
    
    int value = opt.value_or(0); // 如果opt为空,则返回0
    
  6. reset(): 如果 std::optional 中包含值,则销毁该值,使 std::optional 变为空。

    1
    
    opt.reset(); // 之后 opt 将不包含值
    
  7. emplace(): 构造 std::optional 中的值,原来的值如果存在会被销毁。

    1
    
    opt.emplace(10); // 在opt中构造一个int值10
    

使用 std::optional 的正确方式是,在调用 value() 或解引用操作之前,先检查它是否有值,可以使用 has_value() 或者隐式的 bool 转换,这样可以避免潜在的异常或未定义的行为。

参考

本文由作者按照 CC BY 4.0 进行授权