c++23的std::expected的monadic接口是什么? (and_then, or_else)

10次阅读

std::expected 的 and_then 是 monadic 绑定操作:仅当含值时调用函数并返回新 expected,Error 状态则透传;函数参数须为值类型 T,返回值必须是 std::expected(值类型任意、error 类型可变),不支持隐式转换

c++23的std::expected的monadic接口是什么? (and_then, or_else)

std::expected 的 and_then 是什么行为?

and_thenstd::expectedc++23)提供的 monadic 绑定操作:它只在对象处于“含值”状态(即 has_value() == true)时,调用你传入的函数,并将该函数的返回值作为新的 std::expected 返回;如果当前是 error 状态,则直接透传原 error,不执行函数。

关键点:

  • 函数参数类型必须是 T(即 expected 的值类型),不能是引用或 const 限定——标准要求按值捕获,避免生命周期问题
  • 函数返回类型必须是另一个 std::expected(可为不同 error 类型,但值类型任意)
  • 不支持隐式转换;若函数返回 expected,而你当前是 expected,编译器不会自动转成 expected —— error 类型不兼容会直接编译失败
std::expected maybe_int = 42; auto next = maybe_int.and_then([](int x) {     return std::expected{x * 1.5}; }); // next 是 expected

std::expected 的 or_else 什么时候触发?

or_else 是 error 分支的 monadic 对应物:仅当 has_value() == false 时才调用你传入的函数,且该函数必须接收 error 类型(E)并返回另一个 std::expected(值类型必须一致,error 类型可变)。

常见误用:

立即学习C++免费学习笔记(深入)”;

  • 传入函数签名错误,比如写成 [](int err) { ... } 而实际 error 类型是 std::errc → 编译失败
  • 返回类型值类型不匹配:当前是 expected<:string int>,但 or_else 函数返回 expected → 编译失败(T 必须相同)
  • 试图在 or_else 里抛异常:虽然语法允许,但违背 monadic 错误处理契约,应统一用 error 值传递
std::expected maybe_str = std::unexpected{123}; auto recovered = maybe_str.or_else([](int err) -> std::expected {     if (err == 123) return "fallback";     return std::unexpected{err}; });

and_then / or_else 的组合链与类型推导陷阱

连续使用 and_thenor_else 时,每个环节都严格检查返回类型的 TE 是否可拼接。编译器不会帮你做 error 类型归一化 —— 比如前一步返回 expected,下一步 or_else 期望处理 int,就断链了。

实用建议:

  • auto 接收中间结果,避免手动写嵌套模板名(尤其 error 类型长时)
  • 若需统一 error 类型,提前用 std::variant 或自定义 tag 类型封装多种错误,再让所有 or_else 分支返回同一种 E
  • 注意移动语义:传入的 Lambda 若捕获大对象,and_then 内部会 move 调用,确保 lambda 可移动(或用值捕获而非引用)

和传统 if-else 相比,monadic 接口的实际价值在哪?

不是为了炫技,而是解决「深层嵌套错误传播」问题。比如解析 jsON → 提取字段 → 转整数 → 校验范围,每步都可能失败。用 if-else 易写成四层缩进;用 and_then 可扁平表达,且天然短路(任一环节 error 就终止后续)。

但要注意:

  • 调试困难:里看不到中间 and_then 的具体哪步失败,error 值被透传,需靠日志或包装器注入上下文
  • 无法中途修改 error 类型而不显式转换:比如想把 std::Filesystem::error_code 转成自定义 ParseError,必须在 or_else 里手动构造 unexpected
  • 不支持「同时处理 success 和 error」的双分支逻辑(那是 transform + map_error 的事,不是 monadic 接口职责)

真正复杂的错误恢复流程,往往需要 and_thenor_elsetransform 混用,而不是只押注某一个。

text=ZqhQzanResources