std::expected 是 C++23 引入的值优先、错误显式的标准化错误处理工具,用于替代返回码或自定义包装类型,适用于文件解析、配置读取等可预期失败场景,不取代异常。

std::expected 是 C++23 引入的标准化错误处理工具,用来替代传统返回码、异常或自定义包装类型(如 std::optional + 状态码),实现“值优先、错误显式”的函数返回风格。它不是万能异常替代品,而是为预期可能失败但不属异常语义的场景(比如文件解析、配置读取、网络响应解码)提供更清晰、更安全、更可组合的返回接口。
std::expected 的基本结构和构造方式
模板形式为 std::expected<T, E>,其中 T 是成功时携带的值类型,E 是失败时携带的错误类型(通常为 std::error_code、std::string 或自定义错误枚举)。它内部只保存 T 或 E 中的一个,不可同时存在。
- 成功构造:
std::expected<int, std::string> ok{42};或std::make_expected(42); - 失败构造:
std::expected<int, std::string> err{std::unexpect, "file not found"};或std::make_unexpected("timeout"); - 检查状态:
if (res.has_value()) { /* 成功 */ } else { /* 失败,用 res.error() 取错误 */ }
链式调用与错误传播:用 and_then / transform 处理成功路径
当多个操作需按顺序执行、且任一环节失败就终止并透传错误时,and_then 是核心工具。它只在当前为 value 时调用传入的函数,并自动将该函数返回的 expected “扁平化”——避免嵌套 expected<expected<T,E>,E>。
auto res = parse_json(str).and_then(validate_schema).and_then(extract_user_id);- 每个函数都返回
std::expected<NextType, ErrorType>,类型需兼容(错误类型最好一致) - 若某步返回
unexpected,后续函数不执行,整个链直接返回该错误
错误转换与兜底处理:用 or_else 和 value_or
当需要对特定错误做修复、降级或日志记录时,or_else 接收一个以 const E& 为参数的函数,返回新的 expected;而 value_or 提供简单默认值兜底(仅适用于可默认构造或提供默认值的 T)。
立即学习“C++免费学习笔记(深入)”;
auto id = fetch_user_id().or_else([](const std::error_code& ec) { return fallback_user_id(); });int timeout = get_timeout_config().value_or(3000); // 失败时用 3000- 注意:
value_or会强制移动或拷贝T,若T构造代价高或不可拷贝,应改用and_then+ 显式判断
与异常、optional 的关键区别和使用边界
std::expected 不取代异常,也不等价于 std::optional:
- 异常用于真正意外、无法局部恢复的错误(如内存耗尽、硬件故障);
expected用于业务逻辑中可预见、可分类处理的失败(如“用户不存在”、“配置缺失”) -
std::optional只能表达“有/无值”,无法携带错误原因;expected明确区分“成功值”和“失败原因”,支持错误类型多态和模式匹配(配合std::visit自定义访问器) - 函数签名更自文档化:
expected<Data, ParseError> parse(const std::string&)比bool parse(..., Data& out)或抛异常更直观、更易组合
不复杂但容易忽略:别把所有函数都改成 expected,优先用于 I/O、解析、校验等天然带失败语义的函数;保持错误类型轻量、可比较、可输出;搭配 std::error_code + std::system_category() 或自定义错误类别,能更好融入系统生态。









