std::expected链式调用必须用and_then而非transform,因and_then仅在成功时执行回调并透传错误,而transform会无视状态执行导致错误擦除;错误类型须严格匹配,自定义category不可复用std::generic_category。

std::expected 链式调用必须用 and_then,不是 transform
and_then 是推进成功路径的唯一安全方式。它只在 has_value() 为 true 时调用回调,并原样透传上游错误;transform 则无论成功失败都会执行,极易导致错误被“擦除”或转成无关类型。
- 错误示例:
open_file("x").transform([](int fd) { return parse_json(fd); })—— 即使open_file失败,parse_json仍会被调用,返回一个空的std::unexpected或未定义行为 - 正确写法:
open_file("x").and_then([](int fd) { return parse_json(fd); })—— 若open_file返回错误,整个链直接短路,parse_json根本不执行 -
and_then回调参数类型必须严格匹配value_type,返回值类型必须是std::expected<u e></u>,且错误类型E须与上游一致,否则编译失败
std::error_code 构造不能裸写数字,必须指定 category 或用 std::errc
直接写 std::error_code{2, std::generic_category()} 看似可行,但语义脆弱:2 在不同 category 下含义完全不同,且跨平台/跨库时不可靠。
- 优先使用
std::make_error_code(std::errc::no_such_file_or_directory)—— 类型安全、可比对、可序列化 - 对接 C API(如
open())时,先检查返回值是否为 -1,再读errno,并用std::error_code{errno, std::system_category()}封装 - 遇到 EOF 要单独处理:C API 返回 0 或部分读取 ≠ 错误,不能一概转成
std::errc::interrupted或吞掉
IO 链式调用中每个环节都必须返回 std::expected,不能中途裸解包
链式成立的前提是每一步都保持“值 or 错误”的二元结构。一旦用 .value() 或 *result 强行解包,失败时程序直接 std::terminate,彻底破坏链式容错能力。
- 错误写法:
auto fd = open_file("x").value(); parse_json(fd);—— 文件打不开就 crash - 正确模式:所有中间函数签名统一为
std::expected<t std::error_code></t>,全程用and_then推进 - 日志或重试决策需错误信息时,用
or_else捕获失败分支:.or_else([](const std::error_code& ec) { log_error(ec); return std::unexpected(ec); })
自定义错误类型必须继承 std::error_category,不能复用 std::generic_category
std::generic_category 是通用兜底,语义模糊;自定义错误若复用它,会导致 ec.category() == std::generic_category() 无法区分来源,比对和调试全失效。
- 必须定义独立子类,重载
name()和message(),并在全局注册(通常用静态变量触发构造) - 构造
std::error_code时,必须传入该 category 实例,例如:MyErrorCategory::get().default_error_condition(MyErrc::timeout) - std::errc 枚举已覆盖绝大多数系统级错误,业务层错误建议用 enum class + 自定义 category,而非字符串
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











