std::expected 不能直接替代 try/catch,因其不抛异常而无法触发栈展开自动清理资源,仅适用于预期失败的正常业务路径,而非严重故障;需显式检查 has_value() 或用 and_then/or_else 安全链式调用。

std::expected 为什么不能直接替代 try/catch
因为 std::expected 不抛异常,它把错误当作值来传递,所以你没法靠栈展开自动清理资源——比如局部 std::ofstream 或 std::unique_ptr 的析构仍会发生,但调用链上不会自动跳转。它适合“预期可能失败、且失败是正常业务路径”的场景,比如解析配置、读取文件头、网络响应解码,而不是处理内存分配失败这类严重故障。
常见错误现象:std::expected<int, std::string> f() { return std::unexpected("bad"); } 写对了,但后续用 value() 强解包导致 std::bad_expected_access 崩溃——这和 std::optional::value() 一样危险。
- 必须先检查
has_value(),或用and_then()/or_else()链式处理 - 不要在循环里反复调用
value(),尤其当错误路径不是罕见异常而是高频分支时 -
std::expected的error()返回的是 const 引用,修改 error 类型需 move 构造新对象
如何安全链式调用多个返回 expected 的函数
用 and_then 是最自然的写法,它只在有值时调用下一个函数,且自动传播错误类型(要求下游函数返回同 error 类型的 std::expected)。
std::expected<int, std::string> parse_int(std::string_view s) {
try { return std::stoi(std::string(s)); }
catch (...) { return std::unexpected("parse failed"); }
}
std::expected<double, std::string> sqrt_positive(int x) {
if (x < 0) return std::unexpected("negative input");
return std::sqrt(x);
}
auto result = parse_int("42")
.and_then(sqrt_positive); // 类型推导为 std::expected<double, std::string>
注意:如果中间某步返回的 std::expected error 类型不同(比如 std::error_code),and_then 会编译失败。此时要么统一 error 类型,要么用 transform + 手动包装错误。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
transform只映射 value,不处理 error;map_error只映射 error,不碰 value - 想做“失败时 fallback 到默认值”,用
value_or(0.0),但注意这会隐藏错误信息 - 嵌套
and_then深度超过 3 层时,考虑拆成命名变量,避免可读性下降
与 std::optional 和 std::variant 的关键区别在哪
std::expected 明确区分“无值”(std::nullopt)和“有错误”(std::unexpected)——而 std::optional 的 std::nullopt 语义模糊,无法携带错误原因;std::variant<T, E> 虽能表示两种状态,但没提供 and_then、or_else 这类语义化操作接口,且构造时需显式指定索引或标签,易出错。
典型误用:std::variant<int, std::string> v = "timeout"; 看似等价,但后续无法用统一方式提取 int 或 fallback 处理 string 错误——你得手动 std::visit,且每次都要写完整匹配逻辑。
- error 类型必须可复制或可移动,否则
std::expected无法构造(如 non-copyable class)需 wrap 成std::unique_ptr -
std::expected的 value_type 和 error_type 不能相同(编译器禁止),避免歧义 - 若 error 是
enum class,推荐用std::error_code或std::string_view,而非裸 enum,便于调试和日志
在 C++20/23 项目中启用和兼容性注意事项
std::expected 是 C++23 标准特性,GCC 12.1+、Clang 15+、MSVC 19.35+ 原生支持。但很多项目仍在 C++20 下开发,这时得用第三方实现(如 tl::expected),它 API 兼容且 header-only。
常见坑:#include <expected> 在旧标准下直接报错;即使开了 -std=c++23,某些 STL 实现(如 libstdc++ 12)仍缺失部分成员函数(如 transform)。
- 检查编译器和 STL 版本:运行
echo _GLIBCXX_RELEASE(GCC)或clang++ --version - 用
__cpp_lib_expected宏做条件编译,避免硬依赖 - 不要混用
tl::expected和std::expected的类型别名,它们不隐式转换
真正麻烦的不是语法,而是团队对“错误即值”范式的接受度——一旦开始用 and_then,就很难再容忍满屏的 if (!res.has_value()) return res.error(); 手动传播。

















