std::expected 不支持自动扁平化嵌套,and_then 返回 double-wrapped 类型是标准行为;需统一错误类型、立即封装 c api 错误、避免 errno 丢失,并用业务枚举替代混杂错误类型。

std::expected 本身不支持多层嵌套业务异常的自动展开或链式捕获,“嵌套”是设计误用,不是能力缺失;真正要解决的是错误类型不统一、and_then 返回双层包装、以及 C API 错误未及时映射这三类问题。
and_then 返回 double-wrapped std::expected 是编译期事实,不是疏忽
当你写 and_then([](T x) { return f(x); }),而 f(x) 返回 std::expected<u e></u>,结果就是 std::expected<:expected u e>, E></:expected> —— 标准库不做扁平化。这不是 bug,是 C++23 的明确行为。
- 没有
operator>>=,也没有重载的and_then接受返回std::expected的 lambda -
transform同样产出双层,且无论成功失败都执行回调,极易擦除错误 - 想“链下去”,必须让每一步返回的
std::expected的错误类型E完全一致,连const修饰都不能差 - 临时解法:用
.transform([](auto&& v) { return std::move(v); })配合手动std::visit解包,但破坏链式可读性
错误类型 E 不统一,and_then 直接编译失败,不是运行时短路
常见错误是让 parse_json() 返回 std::expected<json std::error_code></json>,而 validate() 返回 std::expected<json std::string></json>,然后试图用 and_then 连接——模板推导立刻报错,错误信息里通常带 no matching function for call to 'and_then'。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
std::error_code和std::string之间即使有隐式转换,模板也不做用户定义转换 - 推荐做法:定义业务专属枚举,如
enum class ConfigError { ParseFailed, SchemaMissing, IoError } - 对接系统调用时,用
std::make_error_condition(std::errc::...)构造,而非裸写std::error_code{2, std::generic_category()} - 若需保留 errno 原始值,可封装为
struct SysError { int err_no; std::error_category& cat; },但所有步骤仍须共用同一E
C API 错误必须在调用点立即封装,不能留到链下游处理
像 open() 或 read() 这类函数,失败后 errno 是全局状态,一旦被后续系统调用覆盖就丢失。你不能指望 and_then 链里的某一步去读 errno 并构造错误。
- 正确模式:
if (fd == -1) return std::unexpected(std::make_error_condition(static_cast<:errc>(errno))</:errc> - EOF 要单独判断:
read()返回 0 不等于错误,不能无条件转成std::errc::interrupted - 不要在
and_then回调里再调一次open()并忽略其返回值——这会掩盖原始失败原因 - 日志增强上下文必须在
or_else里做:.or_else([](auto e) { log("parse_config failed: ", e.message()); return std::unexpected(e); })
最易被忽略的一点:and_then 的回调参数是按值传递的,写成 [](T& x) 会编译失败;而返回值若不是 std::expected,哪怕只差一个 const,也会中断整个链。别指望类型系统帮你“猜意图”,它只做精确匹配。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










