std::expected 是错误值返回协议而非异常处理机制,需手动 try/catch 并封装抛异常的 API,错误类型应选 std::error_code 而非 std::string,且不可与 std::variant 语义互换。

std::expected 不是异常处理机制,而是错误值返回协议
它不替代 try/catch,也不捕获异常——你得自己 try、自己 catch、自己构造 std::unexpected。常见误解是“用了 std::expected 就不用写 try 块”,结果代码在文件打开失败时直接 std::terminate。
关键点:
-
std::filesystem::ifstream构造函数不返回std::expected,它该抛std::filesystem::filesystem_error还是抛 - 所有标准 I/O 函数(
std::fstream、std::filesystem::read_bytes等)目前(C++23)均无原生std::expected版本 - 必须手动封装:
try→ 操作 →if (!stream.is_open())→catch→return std::unexpected(...) - 错误类型选
std::error_code而非std::string:前者支持==、.category()、跨平台映射;后者只能做子串查找,脆弱且无法静态约束错误集合
std::expected 与 std::variant 的根本区别:语义不可互换
std::variant 表示“可能是 A,也可能是 B,二者皆合法”,比如解析配置时字段可为 int 或 std::string;而 std::expected<t e></t> 表示“预期是 T,但可能因失败变成 E”,E 是错误状态,不是业务分支。
误用 std::variant 替代 std::expected 的典型后果:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 调用方无法强制检查错误路径:
std::variant允许访问任一 branch,std::expected强制先调用.has_value()或用if (auto r = f()) { ... }解包 - 无内置短路逻辑:
std::expected::and_then可自然串联多个 IO 操作,std::variant没有等价机制 - 错误类型丢失语义:
std::variant<T, std::string>无法区分“文件不存在”和“JSON 格式错误”,而std::expected<T, std::error_code>可精确比对std::errc::no_such_file_or_directory
std::expected::and_then 的实际约束与坑
它不是语法糖,是严格类型驱动的链式调用。写错参数类型或返回类型,编译器报错晦涩,运行时却不会帮你兜底。
正确用法前提:
- 前一步返回
std::expected<T, E>,lambda 形参必须是T(不是T&,除非你明确要移动) - lambda 必须返回
std::expected<U, E>(E 类型需兼容,通常一致),不能返回裸U或void - 若某步抛异常(比如 lambda 内部未捕获的
std::bad_alloc),and_then不捕获,程序仍会 terminate - 不要在
and_then里调用可能抛异常的函数;如需异常语义,改用.value_or_throw()
文件操作中 std::error_code 的构造必须带 category
直接写 std::error_code{ENOSPC} 是错的——它没指定 category,ENOSPC 在不同系统上含义不同,且无法与 std::errc::no_space_on_device 对齐。
正确做法始终是:
- 读/写失败后取
errno,构造为std::error_code{errno, std::generic_category()} - 对
std::filesystem操作,优先用std::filesystem::status预检,其返回值本身已是std::expected<file_status, std::error_code> - 自定义错误枚举(如
enum class io_errc)应配套实现std::error_category子类,并通过std::error_code{static_cast<int>(e), my_category()}构造
漏掉 std::generic_category() 或硬编码 std::errc 子集,会导致 Windows 和 Linux 下错误码匹配失效,尤其在判断 EROFS 或 ENOTDIR 时静默走错分支。
















