std::expected 的 and_then 用于成功路径的链式组合,只在有值时调用并自动解包返回 expected,错误类型必须一致,不处理异常,需显式返回 unexpected;map 用于无错误转换,or_else 用于错误恢复。

std::expected 的基本组合:operator>> 和链式调用
std::expected 没有内置的 operator>>,但你可以用 and_then(C++23)实现类似 Result 类型的扁平化组合。它只在当前 expected 包含值(即 has_value() == true)时才调用传入的可调用对象,并自动解包返回值——前提是该可调用对象也返回 std::expected。
常见错误是误以为 and_then 会处理错误分支;它完全忽略错误,只对成功路径做变换。若上游失败,整个链直接短路,保留原始错误。
-
and_then接收一个参数为T(即expected<t e>::value_type</t>)的函数,返回std::expected<u e></u> - 返回类型中的错误类型
E必须与原expected一致,否则编译失败 - 不能用
and_then把int转成std::expected<:string std::error_code></:string>再转成std::expected<double std::string></double>—— 错误类型不匹配
std::expected<int, std::string> parse_int(std::string s) {
try { return std::stoi(s); }
catch (...) { return std::unexpected("invalid number"); }
}
<p>std::expected<double, std::string> to_double(int x) {
return static_cast<double>(x) * 1.5;
}</p><p>auto result = parse_int("42").and_then(to_double);
// result 是 std::expected<double, std::string>,值为 63.0</p>and_then 中如何处理可能失败的中间步骤?
如果中间函数本身也可能失败(即返回 std::expected),and_then 正好适配这种“一环扣一环”的错误传递场景。它天然支持嵌套失败:只要任意一环返回 std::unexpected,后续不会执行,最终结果携带该环节的错误。
容易踩的坑是试图在 and_then 回调里 throw 异常——这会导致程序终止(除非你显式捕获并转为 std::unexpected)。标准要求 and_then 内部不传播异常。
立即学习“C++免费学习笔记(深入)”;
- 所有中间逻辑必须显式返回
std::expected,不能靠异常退出 - 若需 fallback 行为(比如“解析失败就用默认值”),应在外层用
value_or或or_else,而非塞进and_then -
and_then不改变错误类型,所以多个步骤共用同一错误类型(如std::string或枚举)会更易维护
std::expected<std::string, std::string> read_file(std::string path) {
std::ifstream f(path);
if (!f.is_open()) return std::unexpected("file not found");
return std::string{std::istreambuf_iterator<char>(f), {}};
}
<p>std::expected<size_t, std::string> count_lines(std::string content) {
size_t n = 0;
for (char c : content) if (c == '\n') ++n;
return n;
}</p><p>// 组合:read → count,任一失败则终止,错误类型保持 std::string
auto lines = read_file("data.txt").and_then(count_lines);</p>和 map、or_else 配合使用的边界情况
and_then 只负责“成功后继续返回 expected”的场景;若你想对值做纯转换(不引入新错误),用 map 更轻量;若想处理错误分支,得靠 or_else。三者分工明确,混用时要注意语义断裂点。
典型误用:用 map 去调用一个可能抛异常的函数,导致未定义行为;或在 or_else 里返回了和原 expected 错误类型不同的类型,引发编译错误。
-
map(f):f接收T,返回U(非 expected),整个结果仍是expected<u e></u> -
or_else(f):f接收const E&,返回std::expected<t e></t>,用于错误恢复 - 三者返回类型都要求错误类型
E严格一致,C++23 标准不支持自动转换
std::expected<int, std::string> get_id() { return 123; }
<p>// map:安全转换,无新错误
auto str_id = get<em>id().map([](int i) { return "ID</em>" + std::to_string(i); });</p><p>// or_else:错误时尝试 fallback
auto fallback = get_id()
.or_else([](const std::string& e) -> std::expected<int, std::string> {
if (e == "not ready") return 0;
return std::unexpected(e);
});</p>实际项目中 and_then 的性能与兼容性提醒
and_then 是零开销抽象:它只是条件调用加移动语义,没有动态分配,也没有虚函数。但它的可用性依赖 C++23 标准库实现。GCC 13+、Clang 16+、MSVC 19.35+ 支持,旧版本需用第三方库(如 TartanLlama/expected)并注意 API 差异(比如有些叫 bind 而非 and_then)。
真正容易被忽略的是错误类型的生命周期管理。如果你用 std::string 作错误,频繁构造临时字符串会影响性能;更稳妥的做法是定义轻量错误枚举,或用 std::error_code 配合自定义 std::error_category。











