应优先用 std::error_code 作 std::expected 的 E 类型,因其可复制/可移动、支持标准枚举和跨平台映射;自定义错误需注册独立 error_category 并重载 message();链式调用必须用 and_then 保障短路语义,禁用 transform 替代;or_else 需显式处理所有错误分支并透传未匹配项。

直接用 std::expected 替代业务函数中裸返回码 + 手动 if 判断的组合,能强制错误路径显式分支、避免漏处理,且零异常开销——但它不是“自动兜底”,错误类型选错或链式调用误用,反而会让逻辑更难 debug。
std::expected 的 E 类型必须满足可复制/可移动
常见错误现象:std::expected<:string std::string> 编译失败,报错类似 static_assert failed: 'E must be copyable and movable'。这是因为部分标准库实现(尤其启用 P0602 后)不把 std::string 视为 trivially copyable;而 std::error_code 是 C++ 标准明确保证可复制/可移动的。
- 优先用
std::error_code:它天然支持std::errc::invalid_argument等枚举,还能跨平台映射系统错误 - 自定义错误枚举时,必须配套注册独立的
std::error_category子类,不能复用std::generic_category() - 绝对不要用
std::string或未声明copyable的 struct 做E,否则编译不过或运行时 UB
and_then 是链式推进的唯一安全方式,别用 transform 替代
当你需要“读配置 → 解析 JSON → 加载资源”这种线性依赖流程时,and_then 是唯一能保证短路语义的操作符。用 transform 会破坏错误传播:即使上游失败,transform 仍会执行回调,导致错误被静默吞掉或转成无关类型。
-
and_then的 lambda 参数是前一步的T值(非引用),类型必须严格匹配;返回值必须是std::expected - 若某步需对成功值做无副作用转换(如加 trace id),再套一层
.transform(),但必须放在and_then链内部,不能替代它 - 禁止在
and_then的 lambda 里throw——std::expected的设计前提就是禁用异常传播
or_else 处理错误分类时,必须显式透传未匹配错误
or_else 不是“兜底 fallback”,而是细粒度错误分流器。典型场景是:对 std::errc::no_such_file_or_directory 自动创建目录并重试,但对 std::errc::permission_denied 必须原样上报。
- lambda 内必须用
if-else或switch显式判断错误码,不能只处理部分分支 - 未匹配的错误必须返回
std::unexpected(err),否则会隐式构造默认T导致逻辑错误 - 别在
or_else里做耗时操作(如网络请求)——它属于错误响应层,不是重试调度器
最容易被忽略的是错误类型的生命周期管理:所有 std::error_code 构造必须指定 category,直接写 std::error_code{2, std::generic_category()} 在不同平台可能语义错乱;而自定义 error_category 忘记重载 message(),会导致日志里只打印数字不显示含义。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











