std::expected

来自cppreference.com
< cpp‎ | utility
 
 
 
 
在标头 <expected> 定义
template< class T, class E >
class expected;
(1)(C++23 起)
template< class T, class E >

    requires std::is_void_v<T>

class expected<T, E>;
(2)(C++23 起)

类模板 std::expected 提供表示两个值之一的方式:它要么表示一个 T 类型的预期 值,要么表示一个 E 类型的非预期 值。expected 决不会无值。

1) 主模板。在自身的存储中包含预期值或非预期值,该值内嵌于expected 对象。
2)void 部分特化。表示一个 void 类型的预期值或在自身的存储中包含非预期值。如果包含非预期值,那么该值内嵌于 expected 对象。

如果程序以引用类型、函数类型,或 std::unexpected 的特化实例化 expected,那么程序非良构。另外,T 必须不是 std::in_place_tstd::unexpect_t

模板形参

T-预期值的类型。类型必须是(可有 cv 限定的)void,或者符合可析构 (Destructible) 要求(尤其是不允许数组或引用类型)。
E-非预期值的类型。类型必须符合可析构 (Destructible) 要求,且必须对于 std::unexpected 为合法的模板实参(尤其是不允许数组、非对象类型及 cv 限定的类型)。

嵌套类型

类型定义
value_typeT
error_typeE
unexpected_typestd::unexpected<E>

成员模板

模板定义
rebind<U>std::expected<U, error_type>

数据成员

成员描述
bool has_valexpected 对象当前是否表示预期值
(仅用于阐述的成员对象*)
T val (仅限主模板)预期值
(仅用于阐述的变体成员对象*)
E unex非预期值
(仅用于阐述的变体成员对象*)

成员函数

构造 expected 对象
(公开成员函数)
销毁 expected 对象以及其所含的值
(公开成员函数)
赋值内容
(公开成员函数)
观察器
访问预期值
(公开成员函数)
检查对象是否含有预期值
(公开成员函数)
返回预期值
(公开成员函数)
返回非预期值
(公开成员函数)
如果有预期值则返回它,否则返回另一个值
(公开成员函数)
如果有非预期值则返回它,否则返回另一个值
(公开成员函数)
单子操作
若存在预期值则返回给定的函数在其上的结果,否则返回 expected 本身
(公开成员函数)
若存在预期值则返回含有变换后的预期值的 expected,否则返回 expected 本身
(公开成员函数)
expected 含有预期值则返回其自身,否则返回给定的函数在非预期值上的结果
(公开成员函数)
若含有预期值则返回 expected 本身,否则返回含有变换后非预期值的 expected
(公开成员函数)
修改器
原位构造预期值
(公开成员函数)
交换内容
(公开成员函数)

非成员函数

比较 expected 对象
(函数模板)
特化 std::swap 算法
(函数)

辅助类

表示一个非预期值
(类模板)
指示对含有非预期值的 expected 的有检查访问的异常
(类模板)
expected 中非预期值的原位构造标签
(类) (常量)

注解

拥有相同功能的类型在 Rust 与 Haskell 中分别被称为 ResultEither

功能特性测试标准功能特性
__cpp_lib_expected202202L(C++23)类模板 std::expected 和关联的辅助类
202211L(C++23)std::expected 的单体式函数

示例

#include <cmath>
#include <expected>
#include <iomanip>
#include <iostream>
#include <string_view>
 
enum class parse_error
{
    invalid_input,
    overflow
};
 
auto parse_number(std::string_view& str) -> std::expected<double, parse_error>
{
    const char* begin = str.data();
    char* end;
    double retval = std::strtod(begin, &end);
 
    if (begin == end)
        return std::unexpected(parse_error::invalid_input);
    else if (std::isinf(retval))
        return std::unexpected(parse_error::overflow);
 
    str.remove_prefix(end - begin);
    return retval;
}
 
int main()
{
    auto process = [](std::string_view str)
    {
        std::cout << "str: " << std::quoted(str) << ", ";
        if (const auto num = parse_number(str); num.has_value())
            std::cout << "值: " << *num << '\n';
            // 如果 num 没有值,那么解引用 num 会造成未定义行为,而
            // num.value() 会抛出 std::bad_expected_access。
            // num.value_or(123) 则使用指定的默认值 123。
        else if (num.error() == parse_error::invalid_input)
            std::cout << "错误:无效输入\n";
        else if (num.error() == parse_error::overflow)
            std::cout << "错误:溢出\n";
        else
            std::cout << "非预期!\n"; // 或调用 std::unreachable();
    };
 
    for (auto src : {"42", "42abc", "meow", "inf"})
        process(src);
}

输出:

str: "42", 值: 42
str: "42abc", 值: 42
str: "meow", 错误:无效输入
str: "inf", 错误:溢出

缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告应用于出版时的行为正确行为
LWG 4141C++23对存储分配的要求难以理解包含的对象必须内嵌于 expected 对象

引用

  • C++23 标准(ISO/IEC 14882:2024):
  • 22.8 Expected objects [expected]

参阅

(C++17)
类型安全的可辨识联合体
(类模板)
(C++17)
可能或可能不保有一个对象的包装器
(类模板)