std::expected专为“结果或错误码”设计,非异常替代品;成功值可隐式转换,失败值须显式用std::unexpected包装;检查用has_value()、取值用value()/error();E不可为引用或数组;优先用.transform()链式处理。

直接用 std::expected<t e></t>,别绕弯——它就是为“返回结果或错误码”这个场景设计的,不是替代异常,也不是加强版 std::optional。
构造时必须区分成功/失败路径
成功值可隐式转换,失败值必须显式用 std::unexpected 包装,漏写会编译失败或误构造为 T:
- ✅ 正确:
return 42;(自动转成std::expected<int std::string>{42}</int>) - ✅ 正确:
return std::unexpected("file not found"); - ❌ 错误:
return "file not found";(编译不过,或触发隐式构造歧义) - ❌ 错误:
return std::string{"file not found"};(会被当成成功值!)
尤其注意:若 E 是 std::error_code,要用 std::make_error_code(...) 构造,别传裸整数;Windows 下 errno 不可靠,得用 GetLastError() 转换。
检查状态别依赖隐式布尔转换
if (e) 虽然合法,但语义模糊,容易和 std::optional 习惯混淆。生产代码应明确分支:
立即学习“C++免费学习笔记(深入)”;
- 用
e.has_value()判断是否成功 - 用
e.value()取值(失败时抛std::bad_expected_access) - 用
e.error()取错(成功时同样抛异常) - 更安全写法:
if (e) { use(e.value()); } else { handle(e.error()); }
避免对临时对象取地址或绑定非 const 引用——std::expected 启用移动语义后,e.value() 可能失效。
错误类型 E 不能是引用或数组
这是编译期硬限制,常见误写:
- ❌
std::expected<int const char></int>(const char*是指针,但语义上常被误当“字符串字面量”,实际不安全) - ❌
std::expected<int char></int>(数组类型非法) - ✅ 推荐:
std::expected<int std::string></int>或std::expected<int std::error_code></int>
若需轻量错误码,定义 enum class errc 并特化 std::is_error_code_enum_v,再配合 std::error_category 实现 what() 和跨模块传播。
链式调用优先用 .transform(),别自己写 and_then
C++23 标准只提供了 .transform()(对应成功路径映射),没提供 and_then 或 or_else。强行手写易出错:
-
.transform()接收一个 callable,返回T'或std::expected<u e></u>,失败时自动短路 - 不要在
.transform()里 throw 异常——它不捕获,会直接逃逸 - 若需失败路径处理,目前只能手动
if (!e) { return std::unexpected(e.error()); } - 第三方库(如
tl::expected)提供完整组合子,但引入依赖前先确认团队是否接受
最常被忽略的一点:std::expected 的错误传播是同步、单层、无上下文堆叠的——它不记录调用栈,也不自动附加文件位置。需要诊断能力时,得在 E 类型里手动存 std::source_location 或 std::string_view 上下文。


















