std::expected链式处理需显式用and_then/or_else构造短路路径,回调签名须严格匹配值类型与错误类型,统一错误域是跨模块可靠协作的前提。

std::expected 的链式错误处理不是靠“自动传播”,而是靠 and_then 显式构造短路路径——上游失败,后续函数根本不会被调用,原始错误原样保留。
and_then 只在有值时执行,且必须返回 std::expected
这是最容易写错的一环:and_then 的回调函数签名必须严格匹配:参数是 T(即 std::expected<t e>::value_type</t>),返回值必须是 std::expected<u e></u> 或 std::expected<u f></u>(错误类型可变,但值类型不能丢)。
- 写成
[](T& x) { ... }会编译失败——and_then按值传递,不接受引用 - 写成
[](T x) { return U{}; }也会失败——返回值不是std::expected,没有隐式转换 - 若上游是
std::expected<int std::errc></int>,回调返回std::expected<double std::string></double>是合法的;但返回std::expected<double std::errc></double>更利于统一错误处理 - 常见误用:用
transform替代and_then,导致错误分支也被回调执行,结果把std::unexpected转成无关类型甚至空值
or_else 处理错误分支,不是“兜底日志”而是可组合恢复
or_else 不是写 std::cerr << "failed" 的地方,它是 monadic 错误恢复入口:只在 has_value() == false 时触发,参数是 E(错误对象),返回值必须是 std::expected<t f></t>(值类型必须和原 std::expected 一致)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 若原类型是
std::expected<:string std::errc></:string>,or_else回调必须返回std::expected<:string ...></:string>,否则编译不过 - 想 fallback 到默认字符串?可以:
or_else([](std::errc e) { return std::expected<:string std::errc>{"default"}; })</:string> - 想重试或降级?也可以:
or_else([](std::errc e) { return load_backup_config(); }),只要load_backup_config()返回同值类型的std::expected - 别在
or_else里 throw 异常——这破坏了整个无异常契约,且不会被捕获
错误类型不匹配是编译期硬伤,不是运行时逻辑问题
链式调用中多个步骤共用不同错误类型(比如有的用 std::errc,有的用 enum class file_error,还有的用 std::string),会导致 and_then 和 or_else 编译失败,因为模板无法推导一致的 E。
立即学习“C++免费学习笔记(深入)”;
- 推荐方案:定义统一枚举类(如
enum class io_error),配合自定义std::error_category,所有 IO 步骤都返回std::expected<t io_error></t> - 临时兼容:用
std::variant包一层错误类型,但会失去==比对和message()支持 - 对接 C API 时别直接用
errno构造std::error_code:先检查返回值是否为 -1,再读errno,并用std::make_error_code(std::errc::...)映射,避免 category 错乱 - 空文件、权限拒绝、路径不存在,在
std::ifstream::is_open()上表现相同,但错误语义完全不同——必须结合std::filesystem::status()或平台 API 补充判断,再映射到统一错误类型
真正难的不是写对 and_then,而是让每个环节的错误类型可比对、可序列化、能跨模块传递——这要求你在设计第一层 std::expected 函数时,就决定好错误域的边界和演化规则。

















