std::optional不能表达业务失败,因其仅表示“有无值”而无法区分失败原因;std::expected通过显式携带领域专属错误类型(如OrderError枚举),使异常可追溯、可审计、可强制检查,并支持and_then链式安全调用。

std::expected 适合处理业务逻辑异常,而 std::optional 不适合——因为它不携带错误上下文,无法区分“无值”和“出错了”。
为什么不能用 std::optional 表达业务失败?
业务异常(如“用户未找到”“余额不足”“参数校验失败”)不是“可选”,而是“本该有、但因条件不满足而失败”。std::optional 只能表达“有没有”,不能回答“为什么没有”。
- 调用方看到
std::optional为空,无法判断是数据库查不到、还是网络超时、还是权限被拒 - 日志、监控、重试策略都缺乏依据,只能靠额外传参或全局状态补救
- 编译器不会强制你检查错误原因,容易写成
if (opt.has_value()) { ... }后直接解包,忽略失败分支
std::expected 如何让业务异常可追溯?
它把“结果”和“失败原因”打包进同一类型,错误不再是隐式状态,而是显式值。
- 错误类型可以是枚举(如
enum class OrderError { NotFound, InsufficientBalance, InvalidState }),语义清晰、可 switch、可序列化 - 也可以是
std::string或自定义结构体,带字段如timestamp、trace_id,方便调试与审计 - 调用方必须调用
.has_value()或模式匹配,否则编译不过(启用-Werror=return-type时)
示例:
std::expected<order ordererror> get_order(int id) {
auto db_result = db::find<order>(id);
if (!db_result) return std::unexpected(OrderError::NotFound);
if (db_result.status != "active") return std::unexpected(OrderError::InvalidState);
return db_result;
}</order></order>
链式调用中,and_then 比层层 if 更安全
多个可能失败的业务步骤(查订单 → 扣库存 → 发通知)若用 std::optional,每步都要手动判空 + 转错误码;而 std::expected 可自然传播错误。
-
and_then只在has_value()时执行下一步,失败则自动短路,返回原错误 -
or_else可做降级:比如“发通知失败”时记录告警但不中断主流程 - 避免嵌套
if (auto o1 = f1(); o1.has_value()) { if (auto o2 = f2(o1.value()); ... }的易错写法
实际项目里最容易被忽略的一点
很多人把 std::expected 当作万能兜底,但它对业务语义表达力很弱——std::errc::permission_denied 和 “用户已被风控” 完全不是一回事。真正的优势在于定义**领域专属错误类型**,并让所有业务函数统一使用它。否则,只是把 errno 换了个壳,没解决根本问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











