std::expected 与 std::optional 的根本区别在于语义:前者明确区分成功(含值t)和失败(含错误e),后者仅表示有值或无值且不携带错误信息。

std::expected 和 std::optional 的根本区别在哪
std::optional 只能表达“有值”或“无值”,它不携带任何失败原因;而 std::expected<t e></t> 明确区分“成功(含 T)”和“失败(含 E)”,E 通常是 std::error_code、std::string 或自定义错误类型。这不是语法糖升级,而是语义跃迁:你不能再靠“有没有值”猜错误,必须显式处理两种分支。
用 std::expected 替代 std::optional 的典型写法
假设原来返回 std::optional<int></int> 表示“计算成功返回结果,失败则空”,现在应改为 std::expected<int std::string></int> 或更规范的 std::expected<int std::errc></int>:
// 旧写法(模糊)
std::optional<int> parse_int(const std::string& s) {
try { return std::stoi(s); }
catch (...) { return std::nullopt; }
}
<p>// 新写法(明确失败原因)
std::expected<int std::errc> parse_int(const std::string& s) {
char<em> end;
errno = 0;
long val = std::strtol(s.c_str(), &end, 10);
if (</em>end != '\0' || errno == ERANGE || val INT_MAX) {
return std::unexpected(std::errc::invalid_argument);
}
return static_cast<int>(val);
}</int></int></p></int>
关键点:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 失败时用
std::unexpected(...)构造,不能直接 return 错误值 - 调用方必须检查
.has_value()或用.value_or(...)(但会丢弃错误)、更推荐用.value()(抛异常)或.error()(安全取错) - 不要把
std::expected<t void></t>当std::optional<t></t>用——这违背设计本意,且 C++23 不允许void作为错误类型
std::expected 常见误用与坑
很多人想“平滑迁移”,结果写出危险代码:
- 直接对
std::expected调用.value()而不检查,失败时抛std::bad_expected_access,比std::optional::value()的异常更易被忽略 - 用
std::expected<t std::string></t>存储长错误信息,但没考虑移动语义开销;高频调用场景建议用轻量枚举(如enum class parse_err { invalid, overflow, empty }) - 在模板函数中硬编码
std::expected<t std::error_code></t>,却忘了std::error_code默认构造是success,导致std::unexpected{}语义不清 - 混用
std::expected和std::variant<t e></t>:后者无访问契约约束,std::expected强制你面对 success/failure 分支
什么时候不该用 std::expected 替代 std::optional
不是所有“可选”都该变成“可预期”。比如:
- 配置项缺失(
std::optional<:string> config_host;</:string>)—— 这真是“无值”,不是“出错”,用std::optional更准确 - 缓存查找未命中(
std::optional<data> cache_get(key);</data>)—— 未命中是正常流程分支,非错误 - 需要隐式转换为
bool判断的场景(if (opt) {...}),std::expected的explicit operator bool()会破坏现有逻辑,需显式写if (exp.has_value())
真正该换的,是那些你原本靠注释或约定说明“返回 nullopt 意味着解析失败/权限不足/网络超时”的函数——现在错误类型本身成了接口契约的一部分。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










