c++oding="utf-8" ?>
std::expected 是为“值或错误详情”建模而生,典型场景是替代传统文件io中丢弃失败原因的做法;它强制处理成功与失败分支,错误类型e推荐用std::error_code以支持跨平台错误分类与比对。

std::expected 替代 std::optional + 异常或返回码的典型场景
传统 C++ 文件 IO 常用 std::ifstream 的 is_open() 或 fail() 判断,再配合手动设错误码、抛异常,或者用 std::optional<:string></:string> 表示“读成功”,但完全丢弃了失败原因。这导致调用方无法区分是“文件不存在”还是“权限不足”或“磁盘满”。std::expected<t e></t> 正是为这种“值 or 错误详情”建模而生——它强制你处理两种分支,且错误类型 E 可以是 std::error_code,天然对接系统级错误。
实操建议:
-
std::expected<:string std::error_code></:string>是最常用组合:成功时带内容,失败时带 POSIX 错误码(如EACCES、ENOENT) - 别用
std::expected<t std::string></t>——字符串无法参与switch、不好比对、丢失错误分类语义 - 构造失败态时,优先用
std::unexpect包装std::error_code(errno, std::generic_category()),而非裸传整数
用 std::expected 封装 std::filesystem::read_file(C++23)
C++23 标准库终于有了 std::filesystem::read_file,但它不返回 std::expected,而是直接抛异常。要获得预期行为,必须自己封装一层。
实操建议:
- 捕获
std::filesystem::filesystem_error,从中提取.code()(即std::error_code),再用std::unexpected构造失败返回 - 不要忽略
.path1()和.path2()——它们在调试时能快速定位是哪个路径出问题,可存入自定义错误结构体(若需更丰富上下文) - 若目标是零异常二进制(如嵌入式或游戏热更新),必须用
open()+read()系统调用重写底层,再转成std::expected;标准read_file本质仍依赖异常传播
示例片段:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
std::expected<:vector>, std::error_code> safe_read_binary(const std::filesystem::path& p) {
try {
return std::filesystem::read_file(p);
} catch (const std::filesystem::filesystem_error& e) {
return std::unexpected(e.code());
}
}</:vector>
链式调用中 and_then 与错误透传的陷阱
很多人想用 and_then 把“读文件 → 解析 JSON → 验证字段”串起来,但容易踩两个坑:一是 and_then 的 lambda 返回类型必须严格匹配外层 std::expected 的 value 类型,二是错误类型不兼容时编译失败,而非静默吞掉错误。
实操建议:
- 所有中间步骤的返回类型统一用
std::expected<t std::error_code></t>,避免混用std::errc、int或自定义枚举(除非显式特化std::is_error_code_enum) - 若某步可能产生新错误类型(如 JSON 解析失败应返回
json_parse_error),要么将其映射到std::errc子集(如std::errc::invalid_argument),要么把整个链的错误类型升级为std::variant<:error_code json_error></:error_code> -
transform比and_then更安全:它只处理 success 分支,失败态自动透传,适合纯数据转换(如std::vector<:byte></:byte>→std::string)
Windows 下 std::error_code 与 Win32 错误码的兼容性细节
在 Windows 上,std::filesystem 内部用 Win32 API(如 CreateFileW),其错误码(如 ERROR_FILE_NOT_FOUND)会被自动转为 std::error_code,但类别是 std::system_category(),不是 std::generic_category()。这意味着直接比对 std::errc::no_such_file_or_directory 会失败。
实操建议:
- 用
ec.default_error_condition()转换后再比对:ec.default_error_condition() == std::errc::no_such_file_or_directory - 跨平台代码中,永远别假设
std::error_code的值等于某个errno数字;只依赖default_error_condition()或category()+value()组合判断 - MinGW 和 MSVC 对此处理一致,但某些旧版 libc++ 可能未完全实现
default_error_condition映射,CI 中务必覆盖 Windows + libc++ 测试
事情说清了就结束。真正麻烦的从来不是怎么写 std::expected,而是决定哪些错误值得暴露、哪些该被上游吞掉、以及错误码语义在不同系统间是否真的一致。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










