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

std::expected 的 and_then 是什么行为?
and_then 是 std::expected(c++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_then 和 or_else 时,每个环节都严格检查返回类型的 T 和 E 是否可拼接。编译器不会帮你做 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_then、or_else 和 transform 混用,而不是只押注某一个。