c++oding="utf-8" ?>
std::expected 是 c++23 引入的用于预期错误场景的值/错误持有类型,适用于解析、i/o 等明确需处理错误的函数式编程;不替代异常,不适用于罕见严重错误。

std::expected 是什么,什么时候该用它
std::expected 是 C++23 引入的标准化错误传递工具,本质是一个持有成功值或错误值的容器类型。它不是用来替代 throw 的通用异常机制,而是为那些「明确知道错误是预期路径一部分」的场景设计的——比如解析配置、读取文件头、调用系统 API 返回 errno。
它适合函数式风格:避免全局状态、不抛异常、把错误当作一等公民参与链式处理。但如果你的错误是罕见的、需要栈展开或跨模块传播的(比如内存分配失败),std::exception 仍是更自然的选择。
如何声明和构造 std::expected
声明时必须指定成功类型和错误类型,两者不能相同(否则编译报错):
std::expected<int std::error_code> parse_int(std::string_view s); std::expected<:string std::string> read_file(std::string_view path); // 错误用字符串描述也合法 </:string></int>
构造方式有三种常见形式:
- 成功值:
std::expected<t e>(value)</t>或std::make_expected(value) - 错误值:
std::unexpected(err),例如std::unexpected(std::errc::no_such_file) - 移动构造:返回临时
std::expected对象时自动触发,无需额外修饰
注意:不要用 std::expected<t e>{value}</t> 初始化列表语法,这会触发聚合初始化,行为未定义。
怎么安全地取值、处理错误、做链式调用
直接访问值前必须先检查状态,否则 .value() 在错误状态下会调用 std::terminate():
- 检查是否成功:
e.has_value()或e.has_value() == true - 安全取值:
e.value()(仅当has_value()为真时) - 安全取错误:
e.error()(仅当has_value()为假时) - 提供默认值:
e.value_or(42),但只适用于错误类型可隐式转换为成功类型的场景(不推荐滥用)
链式调用靠 .and_then() 和 .or_else():
auto result = parse_int("123")
.and_then([](int i) { return std::expected<double std::error_code>(std::sqrt(i)); })
.or_else([](const std::error_code& ec) {
return std::expected<double std::error_code>(std::unexpected(ec));
});
</double></double>
关键点:
-
.and_then()接收一个返回std::expected的 lambda,自动扁平化嵌套 -
.or_else()只在失败时调用,传入的是错误值(不是std::unexpected) - 所有这些操作都要求 lambda 返回类型与外层
std::expected的 success/error 类型严格匹配,否则编译失败
容易踩的坑和兼容性提醒
C++23 标准库实现尚未完全统一:MSVC 19.37+、GCC 13+、Clang 16+ 才完整支持 std::expected。使用前务必确认编译器版本和标准库(如 libstdc++/libc++)支持情况。
常见错误现象包括:
- 编译报错 “no type named ‘expected’ in namespace ‘std’” → 没开
-std=c++23或编译器太旧 -
.and_then()返回类型推导失败 → lambda 返回的std::expected的 error 类型和外层不一致(比如一个是std::error_code,一个是int) - 把
std::expected<void e></void>当作“无返回值的 Result”用 → 合法,但取值只能用.value()(返回 void),不能用.value_or()
另外,std::expected 不提供 .map() 或 .map_error() 这类便利方法(标准没加),得自己封装或依赖第三方(如 tl::expected 的旧版 API),别指望它像 Rust 的 Result 那样开箱即用。
实际写业务逻辑时,最易忽略的是错误类型的粒度控制:用 std::error_code 能复用系统错误码,但自定义错误建议用 enum class + std::error_category,而不是裸 int 或 std::string——后者会让错误传播失去类型安全和可比性。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











