std::expected 不支持多层嵌套异常自动展开,and_then 和 transform 均产生双层包装,需手动解包;错误类型 E 必须统一,推荐使用业务专属枚举或封装结构体保留原始错误语义。

std::expected 本身不支持多层嵌套业务异常的自动展开或链式捕获;所谓“多层嵌套”,本质是错误类型设计问题,不是 std::expected 能力缺陷。
std::expected 的 and_then 不处理嵌套 std::expected
你写 and_then 时传入一个返回 std::expected<u e></u> 的函数,结果得到的是 std::expected<:expected e>, E></:expected> —— 双层包装,不是扁平化结果。标准库没提供自动解包机制。
- 这和 Rust 的
?或 Haskell 的>>=完全不同:C++23 的std::expected没定义operator>>=,也不提供and_then的扁平化重载 -
transform同样会产出双层:若 lambda 返回std::expected,transform就包装一层,不会递归展开 - 想“链式调用并自动传播失败”,必须手动解包:先检查外层是否成功,再检查内层是否成功,否则你就拿着一个
std::unexpected套着另一个std::unexpected
错误类型 E 必须统一,不能混用 std::error_code 和 std::string
常见错误是让 parse 步骤返回 std::expected<t std::error_code></t>,validate 步骤返回 std::expected<u std::string></u>,然后试图用 and_then 连接——编译直接失败。
-
and_then要求所有步骤的错误类型E完全一致,连 cv 限定符都不能差 - 即使
std::error_code可隐式转std::string,模板推导也不做用户定义转换 - 正确做法:定义一个业务专属错误枚举,如
enum class ConfigError { FileOpenFailed, JsonParseFailed, FieldMissing },所有步骤都用它作E - 若需保留原始
std::error_code的 category/value,可封装进结构体:struct IoError { std::error_code ec; std::string context; };
如何在失败路径中追加上下文而不丢失原始错误语义
and_then 天然不记录调用链位置,or_else 也只接收当前 E,无法自动叠加。想让错误带“哪一步出的错”,得主动构造。
立即学习“C++免费学习笔记(深入)”;
- 避免在
or_else里裸调e.error()再拼字符串:比如std::unexpected("parse failed: " + e.error()),会丢掉std::error_code的可比对性(==判定失效) - 更稳妥的方式是用
or_else接住后,构造新错误对象,把原始 error 存为成员:return std::unexpected(ParseError{.cause = e.error(), .step = "validate_config"}); - 若用
std::variant作E,可在每个分支存不同错误源,但注意访问时必须std::visit,不能假设某一种类型一定存在
std::expected 是处理纯副作用操作的正确姿势
当函数没有返回值、只可能失败(比如写日志、发请求、关连接),别硬塞 std::expected<bool e></bool> 或 std::expected<int e></int>。
-
std::expected<void e></void>语义清晰:成功即“完成”,失败即“中断”,且体积最小(无T存储开销) - 它支持
and_then:只要 lambda 参数是void(或省略参数),就能继续链式调用下一步 - 注意:不能对
void类型调用value(),要用has_value()判断,或直接解引用*e(前提是已确认成功) - 和
std::expected<:monostate e></:monostate>不同,void版本是标准特化,保证零成本且语义明确
最易被忽略的一点:std::expected 的移动语义很敏感。在手动实现类似 and_then 的辅助函数时,若对 e.value() 或 e.error() 直接 std::move,可能触发未定义行为——因为它们是左值引用,而某些 T 或 E 类型不可移动。安全写法永远是先判断 has_value(),再分别用 const 引用访问。


















