std::expected 用于 IO 链式调用中逐环节传递成功值或具体错误(如 std::errc::no_such_file_or_directory),禁用异常、支持错误比对与序列化;需用 and_then 推进流程,避免 transform 吞错;错误构造须指定 category,优先使用 std::errc 枚举;对接 C API 时须先检查返回值再读 errno,并正确封装 EOF 与错误。

std::expected 在 IO 链式调用中怎么接住每个环节的错误
它不是用来“吞掉”错误的,而是让每个 IO 步骤能明确返回成功值或具体错误(比如 std::errc::no_such_file_or_directory),且不依赖异常——这对嵌入式、服务端长生命周期进程很关键。
典型链式场景:打开文件 → 读 header → 解析 JSON → 校验字段。任一环节失败,后续都不该执行,但要保留失败原因供日志或重试决策。
- 必须用
std::expected<t std::error_code></t>(不是std::exception_ptr)——std::error_code可比对、可序列化、无栈开销 - 每步函数返回
std::expected,上一步的.value()或.and_then()推进到下一步,别裸解包(否则崩溃) - 不要在 lambda 里 throw;
std::expected的设计前提就是禁用异常传播
and_then 和 transform 怎么选:值传递 vs 错误透传
and_then 是链式核心,它只在有值时调用回调,并原样透传上游错误;transform 则无论成功失败都调用回调,适合做统一包装(比如加 trace id)。
常见错误:用 transform 替代 and_then 导致错误被“吃掉”或转成无关类型:
立即学习“C++免费学习笔记(深入)”;
// ❌ 错误:parse_json 失败时,transform 仍会执行,返回一个空的 expected
auto result = open_file("cfg.json").transform([](auto fd) {
return parse_json(fd); // parse_json 返回 expected<json, std::error_code>
});
// ✅ 正确:and_then 确保 parse_json 只在 open_file 成功时执行
auto result = open_file("cfg.json").and_then([](auto fd) {
return parse_json(fd);
});
std::error_code 构造时容易漏掉 category
直接写 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() - IO 函数返回错误时,用
std::error_code{errno, std::system_category()}比硬编码数字更安全
和 legacy C API(如 open/read/write)对接的坑
C API 返回 -1 表示失败,但 errno 不一定被设(比如 open() 在 O_CLOEXEC 不可用时可能返回 -1 但 errno 未变)。不能无条件读 errno。
- 检查返回值后再读
errno,且用std::error_code{errno, std::system_category()}封装 - 避免把
ssize_t直接当int用:read()返回值可能是 -1(错误)或 0(EOF)或正数(字节数),需分三路处理 - 封装 C 函数时,返回
std::expected<size_t std::error_code></size_t>,把 EOF 视为成功(0 字节),仅 -1 才转错误
复杂点在于:IO 链不是线性流水线,而是带分支的决策流(比如 header 版本号决定走哪条解析路径),这时候 and_then 的嵌套深度会上升,建议用具名 lambda 或拆成小函数,别堆在一行里。否则错误位置难定位,调试时连 std::expected 的 has_value() 都懒得查了。



















