C++中std::expected怎么处理错误_C++23异常处理新方式详解【现代】

3次阅读

std::expected用于预期可能失败的值传递,异常用于意外不可恢复的错误;判断标准是失败是否属于函数正常契约:文件打开失败等应使用std::expected,内存分配失败等应抛异常。

C++中std::expected怎么处理错误_C++23异常处理新方式详解【现代】

std::expected 不是异常处理的替代品,它和 throw/catch 解决的是不同层次的问题:前者用于**预期可能失败的值传递**,后者用于**意外、不可恢复的错误中断**。混用或强行用 std::expected 替代异常,反而会让错误流混乱。

什么时候该用 std::expected 而不是 throw

核心判断标准是「这个失败是否属于函数正常契约的一部分」。

  • ✅ 适合:std::expected —— 例如文件打开失败(std::Filesystem::status)、字符串转数字失败(std::from_chars)、网络请求返回 404 —— 这些是调用者**必须主动检查**的常规分支
  • ❌ 不适合:std::expected —— 例如内存分配失败(newstd::bad_alloc)、溢出、std::vector::at 越界 —— 这些是程序逻辑缺陷或资源耗尽,应由异常中止并向上报告
  • ⚠️ 危险信号:如果写 if (auto r = f(); !r.has_value()) throw std::runtime_Error(r.error().message());,说明你本该直接抛异常,而非绕一圈包装再解包

std::expected 的构造与错误提取容易踩哪些坑?

常见错误不是语法问题,而是语义误用:

  • 别用 std::String 存错误信息 —— std::expected<int std::error_code></int>std::expected<int std::string></int> 更轻量、可比、可跨模块传递;std::error_code 已经覆盖系统/POSIX/标准库错误域
  • 别在 value() 上裸调用 —— 它在无值时抛 std::bad_expected_access,等价于又引入了异常分支;应先用 has_value() 或直接用 and_then/or_else
  • 注意移动语义:若 T(成功类型)不可移动,std::expected<t e></t> 就不能被移动;同理,E 也需满足可复制或可移动约束,否则编译失败

如何链式处理多个 std::expected 返回值?

避免嵌套 if,优先用 and_thenor_else 实现扁平化错误传播:

立即学习C++免费学习笔记(深入)”;

auto parse_config() -> std::expected<Config, std::error_code> {     auto content = read_file("config.json"); // → expected<std::string, ec>     if (!content) return content; // 直接透传错误     return parse_json(content.value()); // → expected<Config, ec> } // 改成: auto parse_config() -> std::expected<Config, std::error_code> {     return read_file("config.json")         .and_then(parse_json); // parse_json 接收 std::string,返回 expected<Config, ec> }

关键点:

  • and_then回调函数必须返回 std::expected(同错误类型),否则编译不过
  • or_else 用于错误恢复:比如 fallback 到默认配置,此时回调应返回 std::expected<t e></t>,而不是裸 T
  • 不支持像 rust 那样自动推导错误类型合并;若中间步骤错误类型不同(如 std::error_code vs int),需手动转换或统一错误类型

为什么 c++23 的 std::expected 还不能完全替代 Result 类型生态?

标准版目前缺失几个工程中高频需要的能力:

  • 没有 map / map_error(仅提供 and_then/or_else),想对成功值做变换还得手动解包再重包
  • 不支持从 std::expected<void e></void> 隐式转为 std::expected<t e></t>(即 no-alloc effect 类型),导致 I/O 类函数难以统一返回风格
  • 没有类似 unwrap_or_else 的便捷取值接口,常用模式仍需写 r.has_value() ? r.value() : fallback()
  • 部分编译器(如 GCC 13.2)对 std::expected 的 constexpr 支持不完整,涉及模板推导时可能编译失败

真正落地时,多数项目会先封装一层薄胶水(比如加 mapunwrap_or),而不是直接裸用标准版。

text=ZqhQzanResources