std::expected是c++23引入的值语义错误传播工具,本质为std::variant封装,不替代try/catch,仅适用于可预期、可分类、需链式传递的业务错误。

std::expected 是什么,它能替代 try/catch 吗
std::expected 是 C++23 引入的值语义错误传播工具,本质是 std::variant<t e></t> 的封装,用于显式表达“成功返回 T 或失败返回 E”——它不抛异常,也不替代 try/catch 处理运行时意外(比如内存分配失败、硬件中断),只适合**可预期、可分类、需链式传递的业务错误**,比如解析失败、文件不存在、校验不通过。
常见误用是把它当 std::optional + 异常混合体:比如在构造函数里 throw,再包进 std::expected,这反而增加开销且破坏语义。它只应在函数签名中明确声明“我可能失败”,且失败原因类型 E 必须是轻量、可复制/移动的(如 std::string_view、enum class、std::error_code)。
如何写一个返回 std::expected 的函数并避免常见陷阱
定义函数时,别直接用 std::expected<int std::string></int>——std::string 有堆分配,频繁构造会拖慢性能;优先用 std::error_code 或自定义 enum:
enum class ParseError { InvalidFormat, OutOfRange };
std::expected<int parseerror> parse_int(std::string_view s);</int>
实现时注意三点:
- 不要在
return std::unexpected(e)前做昂贵操作(比如打开文件后再检查权限);失败路径应尽早退出 - 避免隐式转换:如果返回
std::expected<double parseerror></double>,别让调用方靠.value()强转成int,类型不匹配会在编译期报错 -
std::expected没有operator->,不能像std::optional那样链式调用成员函数;要用and_then或手动解包
如何用 and_then / transform 实现函数式错误链式处理
and_then 是关键:它只在 expected 持有值时调用传入的 callable,并要求该 callable 也返回 std::expected;一旦中间任一环节返回 std::unexpected,后续函数跳过执行,最终结果就是那个失败值。
例如组合解析和校验:
auto result = parse_int("123")
.and_then([](int i) -> std::expected<int parseerror> {
return (i >= 0 && i (i)
: std::unexpected(ParseError::OutOfRange);
});</int>
注意:transform 只映射值(不改变错误类型),适合纯数据转换;and_then 才能引入新错误分支。两者都要求 lambda 返回类型严格匹配——漏写 -> std::expected<...></...> 会导致编译失败,不是静默降级。
容易踩的坑:
- lambda 捕获变量时,若捕获了局部
std::string并在std::unexpected中引用它,对象已析构 → UB -
and_then不支持多参数函数;需要多个输入时,先用std::expected组合结果,再统一处理 - 没有
or_else(C++23 标准未提供),要 fallback 到默认值,得手动判断has_value()
与 std::optional 和 status-code 库的实际对比
std::expected 和 std::optional 定位不同:std::optional 表达“可能无值”,不带错误原因;std::expected 强制你为每个失败提供具体类型 E,这对 API 设计是约束也是保障。
相比 absl::Status 或 outcome::result 这类第三方库,std::expected 更轻量(无状态机、无堆分配),但缺失一些实用功能:
- 没有内置的
status.ToString(),错误信息得靠std::format或自定义to_string(ParseError) - 不支持嵌套错误(比如“网络超时导致解析失败”这种因果链),需靠
std::variant<parseerror networkerror></parseerror>手动建模 - 没有全局错误上下文(如 trace ID),日志追踪得额外传参或依赖外部机制
真正复杂的服务层错误流,std::expected 往往只是最外层接口契约;内部仍需配合 std::error_code 分类、日志埋点、监控指标,才能落地。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











