std::expected不是自动错误传播器,必须每步显式检查has_value(),否则value()调用会抛std::bad_expected_access;错误类型E须可复制/可移动(推荐std::error_code);and_then要求严格类型匹配与短路语义。

std::expected 不是自动错误传播器,它不会帮你跳过检查、也不会吞掉错误——你必须每一步都显式判断 has_value(),否则调用 value() 会直接抛 std::bad_expected_access 崩溃。
为什么 std::expected::value() 一用就崩
常见错误现象:连续调用多个返回 std::expected 的函数后,只在最后判一次 has_value(),中间任意一步失败都会导致后续 value() 访问非法内存。
-
std::expected是纯值语义类型,不介入异常机制;它不捕获、不转换、不兜底任何异常 - 若函数内部
throw但没用try/catch包裹,程序直接终止,根本不会构造std::unexpected -
value()是“信任接口”:它假设你已确认有值,否则行为未定义 - 临时对象 + 多次
value()调用易产生悬垂引用,尤其当返回的是std::expected<T&, E>时
and_then 链式调用总失败编译或静默吞错
典型错误是误用 transform 替代 and_then,或错误类型不统一。
-
and_then只在has_value() == true时执行 lambda,失败则原样透传错误;transform无论成功失败都执行回调,错误会被覆盖或转成无关类型 - 所有链上函数的错误类型
E必须完全一致(含 cv 限定、引用性),混合std::error_code和std::string会导致模板推导失败 - lambda 形参类型必须严格匹配上游
T:上游返回std::expected<int, E>,形参就得是int或int&&,不是const int&(除非你显式声明) - lambda 返回值必须是
std::expected<U, E>,写return x * 2;或return res.value();直接编译失败
错误类型选错导致编译不过或运行时 UB
std::expected 对错误类型 E 有硬性要求:必须可复制、可移动、可比较。很多自定义类型踩坑于此。
立即学习“C++免费学习笔记(深入)”;
-
std::string在部分标准库实现中不被视为 trivially copyable,std::expected<int, std::string>可能触发static_assert failed: 'E must be copyable and movable' -
std::exception_ptr不满足 copy_constructible 要求,不能作E;试图塞进任意异常只会让程序崩溃 - 推荐用
std::error_code:它被标准保证可复制/可移动,天然支持std::errc::invalid_argument等枚举,还能跨平台映射系统错误 - 自定义错误枚举必须配套注册独立
std::error_category子类,不能复用std::generic_category()
多分支 fallback 怎么写才不漏路径
没有 ? 操作符,也没有内置 or_else(C++23 中 or_else 是成员函数,但主流编译器支持仍不稳定),必须手动展开。
- 不要写
if (auto r = f()) { ... } else if (auto r2 = g()) { ... }—— 这里r2类型无法推导,且g()会在f()失败时无条件执行,破坏短路语义 - 正确做法是逐层嵌套:
if (auto r = f()) return r.value(); else if (auto r2 = g()) return r2.value(); else return default_fallback(); - 每个
else分支必须返回同类型的std::expected,不能返回裸值(如return Config{};),否则类型不匹配 - fallback 函数本身也应返回
std::expected<T, E>,保持整条链类型一致,避免隐式构造默认T导致逻辑错误
关键点在于:错误类型一旦定下,就贯穿整条调用链;and_then 不是语法糖,它是类型系统强制的短路契约;而任何想绕过 has_value() 检查的写法,最终都会在某个深夜报出 std::bad_expected_access。


















