std::expected 不支持嵌套错误处理,因嵌套会导致类型断裂、and_then 失效、错误路径难调试;应统一错误类型 E(如 enum class),用 or_else 封装上下文,C API 错误需立即映射 errno。

std::expected 本身不支持嵌套错误处理——所谓“嵌套”,其实是误把多个失败原因当成多层 std::expected 套娃,这会导致类型断裂、and_then 失效,还让错误路径难以调试。
为什么 std::expected 不能嵌套使用
常见错误现象:std::expected<:expected std::errc>, std::string> 这类写法编译不过,或导致 and_then 接收不到预期的 int 参数;更隐蔽的问题是,上游返回 std::unexpected(std::errc::no_such_file),下游却试图用 std::string 接收错误,类型不匹配直接中断链式调用。
-
and_then要求回调函数返回与当前E类型一致的std::expected,嵌套会破坏这个契约 - 嵌套后需手动
.value()解包两层,一旦漏判has_value()就触发未定义行为 -
std::error_code或std::errc本就可携带 category 和 value,没必要靠嵌套来“分层表达”
多个失败路径必须统一错误类型 E
比如文件操作链:打开失败、JSON 解析失败、字段校验失败——它们不是“不同层级”,而是同一业务流程中不同环节的失败点,应映射到同一个 E 类型里。
- 推荐用
enum class FileOpError { OpenFailed, ParseFailed, SchemaMismatch },而不是混用std::errc和std::string - 若需兼容系统调用错误,用
std::variant<:errc fileoperror>,但注意所有链上函数必须返回完全相同的E类型 - 对接 C API 时,必须在调用点立刻转换:
if (fd == -1) return std::unexpected(std::make_error_condition(static_cast<:errc>(errno)),不能留到链下游再处理
and_then 链中如何保留并增强错误上下文
and_then 天然透传原始错误,不会自动追加位置信息。想让日志知道“是 parse_config 还是 load_resources 挂了”,得手动封装。
- 不要在 lambda 里裸调
result.error()再构造新std::unexpected,容易丢掉std::error_code的category和value - 推荐用
or_else接住错误后做轻量包装:例如or_else([](auto err) { return std::unexpected(add_context(err, "parse_config")); }),其中add_context返回同类型的E - 若需拼接字符串(如
"failed to parse config.json: invalid UTF-8"),接受它会失去可比对性——仅用于日志,别用于控制流分支判断
对接 C 系统调用时 errno 映射的硬约束
errno 是全局变量,且只在系统调用失败后有效;延迟读取或跨函数传递会导致值被覆盖或未定义。
- 每次调用
open()、read()、close()后必须立即检查返回值,再立刻读errno - 用
std::make_error_condition(static_cast<:errc>(errno))转成标准语义,别手写数字(如std::error_code{13, std::generic_category()}) - EOF 不是错误,
read()返回 0 表示正常结束;只有返回 -1 且errno != 0才算失败
真正难的不是写 and_then 链,而是设计出能覆盖所有失败点又不膨胀的 E 类型——它得足够细(区分 no such file 和 permission denied),又足够稳(不随每个新错误加一个枚举项就改接口)。多数项目卡在这一步,而不是语法。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











