std::expected未定义operator>>=,仅提供and_then和or_else实现编译期类型严格匹配的链式调用;错误类型不一致直接导致模板推导失败,无法运行时短路。

标准 std::expected 不支持 operator>>=,链式调用必须用 and_then 和 or_else,且错误类型必须严格一致——编译期就卡住,不是运行时“短路”失败。
为什么 std::expected 没有 operator>>=
C++23 标准库里的 std::expected **根本没定义 operator>>= 或任何 monadic 绑定操作符**。你看到的 e1 >>= f >>= g 写法,要么来自第三方库(如 tl::expected),要么是误把 Rust/Haskell 语法套到 C++ 上了。
std::expected 只提供两个成员函数:and_then 和 or_else,它们是纯函数式风格的立即分支选择:
-
and_then只在has_value() == true时调用,传入成功值,返回另一个std::expected -
or_else只在has_value() == false时调用,传入错误值,返回另一个std::expected - 两者都不抛异常、不重试、不延迟求值
and_then 链式调用失败的典型原因
连续 and_then 调用能跑通的前提,是每一步返回的 std::expected 类型必须被下一层签名接受。常见报错是模板推导失败,错误信息往往很长,但核心就一条:错误类型不匹配。
立即学习“C++免费学习笔记(深入)”;
假设三步函数:
std::expected<int, std::string> parse_input(std::string); std::expected<double, std::string> validate(int); std::expected<bool, std::string> execute(double);
这个链可以跑:
auto result = parse_input("42")
.and_then(validate)
.and_then(execute);
但只要其中任意一个改成 std::error_code:
-
validate返回std::expected<double, std::error_code> -
and_then就会编译失败 —— 即使std::error_code可隐式转成std::string,模板推导也不做用户定义转换
解决办法只有两个:
- 统一所有步骤的错误类型(推荐)
- 在中间用
or_else显式构造新std::expected,但会打断链式结构
and_then vs transform:别吃掉错误
在 IO 或解析类链式调用中,and_then 是推进流程的核心;transform 是包装器,它**无论成功失败都会执行回调**,容易掩盖错误。
比如读文件后解析 JSON:
// ❌ 错误:open_file 失败时,transform 仍会执行,parse_json 不该被调用
auto result = open_file("cfg.json").transform([](auto fd) {
return parse_json(fd);
});
// ✅ 正确:and_then 确保 parse_json 只在 open_file 成功时执行
auto result = open_file("cfg.json").and_then([](auto fd) {
return parse_json(fd);
});
关键区别:
-
and_then:有值才调用,错误原样透传 -
transform:总要调用,可能把错误转成无关类型(比如返回std::expected<void, std::string>而不是原始std::error_code)
std::error_code 构造时 category 不能省
用 std::error_code 作错误类型时,直接写 std::error_code{2, std::generic_category()} 是危险的。数字 2 在不同 category 下语义完全不同:
-
std::generic_category()的2是std::errc::no_such_file_or_directory -
std::system_category()的2是ENOENT(语义一致只是巧合)
正确做法:
- 优先使用
std::errc枚举:std::make_error_code(std::errc::invalid_argument) - 自定义错误必须注册独立
std::error_category子类,不能复用std::generic_category() - 对接 C API 时,先检查返回值,再读
errno,并注意区分 EOF 和真实错误
最易被忽略的一点:类型一致性不是运行时契约,而是编译期硬约束。一旦某层 and_then 的返回类型和下一层期望不匹配,整个链就断在编译阶段——没有警告,只有模板错误瀑布。


















