std::filesystem::read_file 不能直接替代旧文件IO代码,因其失败时抛出异常而非返回错误码,且不提供 errno 等细粒度错误信息;必须手动封装为 std::expected 并保留路径、操作类型等上下文。

std::expected + std::filesystem::read_file 是 C++23 文件 IO 重构的主力组合,但必须手动封装——标准库的 read_file 本身不返回 std::expected,直接用会丢失错误细节。
为什么不能直接用 std::filesystem::read_file 替代旧代码
它在失败时直接抛 std::filesystem::filesystem_error,而老项目往往禁用异常、或要求区分 ENOENT(文件不存在)和 EACCES(权限拒绝)等具体原因。裸调用等于把错误分类能力交还给调用方手动 catch,没解决“丢弃错误语义”的根本问题。
- 别写
auto data = std::filesystem::read_file(p);就完事——这行代码一旦失败,整个调用栈就崩了 - 必须捕获
std::filesystem::filesystem_error,从中提取.code()构造std::unexpected -
.path1()和.path2()字段在调试时极有用,比如符号链接解析失败时能立刻看出是源路径还是目标路径出问题
std::expected<:string std::error_code> 是最稳妥的返回类型
它既保留 POSIX 错误码的可比性(支持 switch、==),又对接 std::errc 枚举(如 std::errc::no_such_file_or_directory),还能跨平台映射 Windows 的 ERROR_FILE_NOT_FOUND。
- 别用
std::expected<:string int>:整数无法携带 category,errno == 2在 Linux 是ENOENT,在 Windows 可能是别的含义 - 构造失败态时,用
std::unexpected(std::error_code{errno, std::generic_category()}),不是std::unexpected(errno) - 如果读的是二进制内容,返回
std::expected<:vector>, std::error_code>更准确,避免std::string对非 UTF-8 内容的隐式假设
用 and_then 链式处理多步文件操作
旧代码里常见三层嵌套 if (ok) { ... } else { return err; },极易漏掉某次 return 导致未定义行为;and_then 天然短路且类型安全。
-
and_then的 lambda 参数必须是前一步的 success 值类型,比如上一步返回std::expected<:string e>,lambda 形参就得是std::string,不是const std::string&(除非你明确想避免移动) - lambda 内部不能
throw;若某步需要异常语义(比如解析失败要 abort 整个流程),改用value_or_throw()显式转换 - 连续调用
read_file → parse_json → validate_schema时,任意一步失败,后续函数根本不会执行,也不用担心资源泄漏——每步都只在成功时才拿到值
底层仍需系统调用时,open() + read() 比 std::ifstream 更可控
当目标是零异常二进制(如嵌入式、游戏热更新模块),std::ifstream 的内部实现仍依赖异常传播机制(即使你没开启 exceptions()),而 open()/read() 系统调用返回 -1 并设 errno,天然适配 std::error_code。
- 别在
std::ifstream构造后只检查is_open()就返回——它可能因EACCES失败,但errno已被覆盖;应紧接open()后立即读errno - 用
std::make_error_code(static_cast<:errc>(errno))转换更安全,它会做平台适配,比std::generic_category()更精确 - 二进制读取务必指定
O_RDONLY | O_CLOEXEC标志,避免文件描述符泄露;C++23 的std::filesystem::read_file默认不设CLOEXEC,这点反而不如手写系统调用
真正难的不是写对一行 std::expected,而是确保每个失败分支都携带足够诊断信息——比如文件路径、操作类型、原始 errno、甚至 mmap 失败时的 MAP_FAILED 地址。这些上下文一旦在第一层封装里丢掉,后面再补就是徒劳。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










