std::expected 是专为携带错误值设计的类型,用于替代 std::pair 或异常处理,成功时含 T、失败时含非 void 的 E;应避免误用 .value() 和混用 std::optional,推荐用 and_then 实现链式调用,map/map_error 分别转换成功/失败值,error_or 提供错误兜底。

std::expected 不是“更安全的 std::optional”,它专为携带错误值设计,适合替代返回 std::pair<t error></t> 或抛异常的函数式错误处理场景。
std::expected 的构造与基本判别逻辑
它本质是 std::expected<t e></t>:成功时含 T,失败时含 E(E 必须是可复制/可移动的非 void 类型)。不能用 std::expected<int void></int> —— 那该用 std::optional<int></int>。
常见误用是直接用 std::make_expected(不存在),正确方式是:
std::expected<int, std::string> parse_int(const std::string& s) {
try {
return std::stoi(s); // 构造成功分支
} catch (...) {
return std::unexpected("invalid number format"); // 显式构造失败分支
}
}-
return T{}→ 自动推导为成功分支 -
return std::unexpected(E{})→ 明确表示失败,E类型必须匹配模板参数第二项 - 不要用
.value()直接取值 —— 它在失败时抛std::bad_expected_access,和异常无异,违背函数式初衷
用 and_then 实现链式调用(类似 Rust 的 ? 或 Haskell 的 >>=)
and_then 是实现“成功才继续”的核心:它只在 expected 处于就绪状态时调用传入的函数,并将该函数的返回值(也必须是 std::expected)原样返回;失败则短路透传原错误。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
auto read_config() -> std::expected<std::string, std::string> { /* ... */ }
auto parse_config(const std::string&) -> std::expected<Config, std::string> { /* ... */ }
<p>auto config = read_config().and_then(parse_config);
// config 是 std::expected<Config, std::string>
// 若 read_config() 失败,parse_config() 根本不执行- 传给
and_then的函数签名必须返回std::expected<U, E>,且E类型要和外层一致(或可隐式转换) - 不支持自动类型提升,比如
and_then([]{ return 42; })编译失败 —— 必须返回std::expected - 若需处理不同错误类型,得先用
map_error统一转换,否则链会断
map、map_error 与 error_or 的实际分工
这三个是“转换”操作,但语义清晰分界:
-
.map(f):仅当成功时对T应用f,返回新std::expected<U, E>(f返回U) -
.map_error(f):仅当失败时对E应用f,返回新std::expected<T, F>(f返回F) -
.error_or(default_e):不是转换,而是兜底取值 —— 成功时返回default_e,失败时返回内部错误值。适合日志 fallback 或默认错误码
典型陷阱:误以为 map 能处理失败路径 —— 它完全忽略错误分支,失败时原样透传,不会调用 f。
和异常、std::optional 混用时的关键边界
std::expected 不是万能胶。它和异常共存时,需明确责任边界:
- 底层 I/O、内存分配等不可恢复错误仍建议抛异常(如
std::system_error),std::expected更适合“预期内可能失败”的业务逻辑(如解析、校验、查找) - 不要把
std::optional<T>和std::expected<T, std::monostate>当作等价 —— 后者语义冗余,且std::monostate无法携带错误信息 - 跨 DLL 边界或 ABI 稳定性要求高时慎用:
std::expected是 C++23 新特性,各标准库实现细节(如内存布局、移动语义)尚未完全收敛
最易被忽略的一点:std::expected 的拷贝/移动成本取决于 T 和 E —— 如果其中一个是大对象,链式调用中频繁传递可能引发意外开销,必要时用 std::expected<const T&, E> 或指针包装。

















