std::expected的and_then是C++23中用于短路链式调用的接口,专为返回std::expected的函数设计,执行扁平化操作:成功时调用函数并展开其返回的expected,失败时直接传递error;而map仅转换value且不处理error传播。

std::expected 的 and_then 是什么,和 map 有什么区别
and_then 是 C++23 中 std::expected 提供的「短路链式调用」接口,专门用于处理**返回另一个 std::expected 的函数**。它不像 map 那样只转换值,而是真正做“扁平化”:如果当前是 error,直接跳过函数调用,原样返回 error;如果当前是 value,才调用函数,并把函数返回的 std::expected 拆出来——相当于手动写 if (has_value()) return f(value()); else return unexpected(error());。
常见错误现象:and_then 编译失败,往往是因为传入的 lambda 或函数返回类型不是 std::expected<T, E>(比如忘了加 return、或返回了裸 T)。
- 使用场景:连续多个可能失败的操作,比如「读配置 → 解析 JSON → 校验字段」,每一步都可能返回
std::expected<T, std::string> - 参数差异:它只接受一个可调用对象,且该对象必须接收
value_type&&(即当前成功的值),返回std::expected<U, E>,其中E必须和原expected的 error 类型一致(或可隐式转换) - 性能影响:零开销抽象,不额外分配内存,只是条件分支 + 移动构造
怎么写一个能被 and_then 接收的函数
关键不是“怎么调用”,而是“怎么定义后续函数”——它必须显式返回 std::expected,不能靠自动推导或隐式转换。
示例(错误写法 vs 正确写法):
立即学习“C++免费学习笔记(深入)”;
// ❌ 错误:返回裸 int,编译不过
auto parse_int = [](std::string s) -> int {
try { return std::stoi(s); }
catch (...) { return -1; } // 还没包装成 expected!
};
<p>// ✅ 正确:返回 std::expected,error 类型和上游一致
auto parse_int = [](std::string s) -> std::expected<int, std::string> {
try { return std::stoi(s); }
catch (...) { return std::unexpected("invalid number format"); }
};</p>- 容易踩的坑:lambda 返回类型省略时,编译器无法从多条 return 路径推导出
std::expected,必须显式写-> std::expected<...> - 兼容性注意:GCC 13+ / Clang 16+ 才完整支持 C++23
std::expected及and_then;MSVC 19.35+ 支持但需/std:c++23 - 如果 error 类型不一致(比如上游是
std::errc,下游想用std::string),and_then不会自动转换,得先用transform_error统一
嵌套 and_then 和错误传播的实际表现
连续调用多个 and_then 时,只要任意一步返回 unexpected,后续所有 and_then 都不会执行,最终结果就是那个最先出现的 error —— 这就是「短路」,也是它比手写 if-else 更可靠的地方。
示例链式调用:
std::expected<std::string, std::string> load_config() { /* ... */ }
auto parse = [](std::string s) -> std::expected<int, std::string> { /* ... */ };
auto check = [](int x) -> std::expected<bool, std::string> { return x > 0 ? true : std::unexpected("non-positive"); };
<p>auto result = load_config()
.and_then(parse)
.and_then(check);
// 如果 load_config() 失败 → result 是那个 error
// 如果 parse() 失败 → result 是 parse 的 error,check 不执行
// 如果 check() 失败 → result 是 check 的 error</p>- 不要试图在
and_then里 throw 异常:虽然语法允许,但会终止整个链并抛出,破坏预期的 error 值传递语义 - error 类型必须严格匹配(或可转换),否则第二层
and_then编译失败 —— 比如上游是std::expected<int, std::errc>,下游函数返回std::expected<double, std::string>,就过不了 - 返回的
std::expected中的 value 类型可以不同(int→bool),但 error 类型链必须一致
为什么不用 std::optional + 手动检查,而要用 and_then
因为 std::optional 不带 error 信息,一旦出错只能丢弃上下文;而 std::expected 的 and_then 把「成功路径的自然流动」和「错误路径的精准保留」绑在一起,不靠异常、不靠全局状态、也不靠重复的 if (!x.has_value()) return x.error();。
- 真实代价:多写几个
std::expected<..., ...>模板参数,但换来的是类型安全的错误源头追踪 - 容易被忽略的点:
and_then返回的是新std::expected,不是引用,所以别指望它能修改原对象;所有操作都是纯函数式风格 - 复杂点在于 error 类型管理——项目初期定好 error 类型(比如统一用
enum class Error),后面加and_then才不会反复卡在类型不匹配上











