c++如何使用std::expected处理错误_c++ 23返回值与错误码封装【方法】

发布时间 - 2025-12-30 00:00:00    点击率:
std::expected适用于可预见的业务失败场景,如文件打开失败、网络请求错误等,需显式处理而非异常;构造须指定成功/失败态,取值应避免直接调用value()以防终止。

std::expected 是 C++23 中真正可用的“结果类型”,它比手动传 std::error_code* 或抛异常更轻量、更可控,但用错地方反而会让错误处理更混乱。

什么时候该用 std::expected 而不是异常?

适合明确区分“预期失败”和“意外崩溃”的场景:比如文件打开失败(路径不存在、权限不足)、网络请求返回 404/503、JSON 解析字段缺失——这些是业务逻辑中可预见、需分支处理的失败,不是程序 bug。

  • 函数签名清晰暴露可能失败:std::expected<:string std::errc>std::string + 注释“失败时抛 std::system_error”更可靠
  • 调用方无法忽略错误:必须显式检查 .has_value() 或用 .value_or() / .and_then()
  • 避免异常开销或禁用异常环境(嵌入式、游戏引擎底层)
  • 不适用于需要栈展开清理资源的场景(比如构造函数中途失败且持有 RAII 对象)

std::expected 的基本构造与取值方式

不能像 std::optional 那样默认构造成功值;必须显式指定成功或失败态。常见误区是误用 value() 导致未定义行为。

  • 构造成功值:std::expected{42}std::make_expected_from_current_exception(42)
  • 构造失败值:std::expected{std::unexpect, std::errc::no_such_file_or_directory}
  • 安全取值:e.value_or(-1)(失败时返回默认值),e.has_value() ? e.value() : handle_error(e.error())
  • 危险操作:e.value()e.has_value() == false 时直接 std::terminate,无异常抛出
auto read_config() -> std::expected {
    std::ifstream f{"config.json"};
    if (!f.is_open()) {
        return std::unexpected{std::errc::no_such_file_or_directory};
    }
    std::string content{std::istreambuf_iterator{f}, {}};
    return content;
}

// 使用
auto result = read_config();
if (result) {
    parse_json(result.value());
} else {
    std::cerr << "Config load failed: " 
              << std::make_error_code(result.error()).message() << "\n";
}

链式调用:用 .and_then() 替代嵌套 if

.and_then() 接收一个返回 std::expected 的函数,自动短路失败路径;比手写 if (ok) { auto r2 = f2(); if (r2) ... } 更紧凑,也避免忘记检查中间步骤。

  • 函数参数类型必须匹配:若 estd::expected,则 e.and_then(f) 要求 f 签名为 T → std::expected(错误类型必须一致)
  • 不支持跨错误类型转换,如需统一错误码,提前用 std::variant 封装或转换为通用枚举
  • .map() 仅转换成功值,不改变是否失败;.or_else() 仅在失败时执行,用于 fallback 或日志
auto load_and_validate() {
    return read_config()
        .and_then([](std::string s) -> std::expected {
            auto cfg = parse_json(s);
            if (!cfg.is_valid()) {
                return std::unexpected{std::errc::invalid_argument};
            }
            return cfg;
        })
        .and_then([](Config c) -> std::expected {
            if (!c.has_required_field()) {
                return std::unexpected{std::errc::state_not_recoverable};
            }
            return c;
        });
}

与旧式错误码(std::error_code)的互操作要点

std::expected 不自动兼容 std::error_code;它的错误类型是模板参数,需显式选择。用 std::errc 最轻量,但缺乏自定义上下文;用 std::error_code 需注意 category 生命周期。

  • 推荐起始方案:std::expected —— 标准错误码,零成本,适合系统调用封装
  • 需要自定义错误信息时:std::expected,但必须确保 category 全局存活(例如用 std::generic_category() 或静态注册的 category)
  • 避免 std::expected:无法参与错误分类、比较、国际化,且拷贝开销大
  • 不要把 std::exception_ptr 塞进 expected:失去静态类型优势,又没获得异常的栈展开能力

最易被忽略的是错误类型的传播一致性:一旦在某个接口用了 std::expected,下游所有 and_then 必须保持相同错误类型,否则编译失败。这不是缺陷,而是强制你提前设计错误域边界。


# js  # json  #   # ai  # c++  # stream  # String  # if  # 封装  # 构造函数  # Error  # auto  # 接口  # map  # 类型转换  # 对象  # bug  # 自定义  # 链式  # 错误码  # 的是  # 什么时候  # 要把  # 用了  # 这不是  # 不存在  # 会让 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Laravel Admin后台管理框架推荐_Laravel快速开发后台工具  Laravel如何使用Sanctum进行API认证?(SPA实战)  uc浏览器二维码扫描入口_uc浏览器扫码功能使用地址  Laravel怎么配置自定义表前缀_Laravel数据库迁移与Eloquent表名映射【步骤】  Laravel如何配置和使用队列处理异步任务_Laravel队列驱动与任务分发实例  Laravel如何处理CORS跨域请求?(配置示例)  QQ浏览器网页版登录入口 个人中心在线进入  PHP 实现电台节目表的智能时间匹配与今日/明日轮播逻辑  Laravel如何监控和管理失败的队列任务_Laravel失败任务处理与监控  如何在香港服务器上快速搭建免备案网站?  Laravel Seeder怎么填充数据_Laravel数据库填充器的使用方法与技巧  Laravel中间件如何使用_Laravel自定义中间件实现权限控制  网页设计与网站制作内容,怎样注册网站?  node.js报错:Cannot find module &#39;ejs&#39;的解决办法  微信小程序 HTTPS报错整理常见问题及解决方案  Midjourney怎样加参数调细节_Midjourney参数调整技巧【指南】  HTML5段落标签p和br怎么选_文本排版常用标签对比【解答】  专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?  Laravel中Service Container是做什么的_Laravel服务容器与依赖注入核心概念解析  Laravel如何自定义分页视图?(Pagination示例)  如何在 Go 中优雅地映射具有动态字段的 JSON 对象到结构体  如何在云主机上快速搭建多站点网站?  猎豹浏览器开发者工具怎么打开 猎豹浏览器F12调试工具使用【前端必备】  html5源代码发行怎么设置权限_访问权限控制方法与实践【指南】  Laravel怎么实现模型属性的自动加密  Laravel怎么进行数据库事务处理_Laravel DB Facade事务操作确保数据一致性  Laravel Eloquent模型如何创建_Laravel ORM基础之Model创建与使用教程  专业商城网站制作公司有哪些,pi商城官网是哪个?  Laravel怎么集成Vue.js_Laravel Mix配置Vue开发环境  如何打造高效商业网站?建站目的决定转化率  Win11搜索栏无法输入_解决Win11开始菜单搜索没反应问题【技巧】  Laravel如何安装Breeze扩展包_Laravel用户注册登录功能快速实现【流程】  php读取心率传感器数据怎么弄_php获取max30100的心率值【指南】  如何用搬瓦工VPS快速搭建个人网站?  Java解压缩zip - 解压缩多个文件或文件夹实例  Windows10怎样连接蓝牙设备_Windows10蓝牙连接步骤【教程】  Java遍历集合的三种方式  Laravel怎么设置路由分组Prefix_Laravel多级路由嵌套与命名空间隔离【步骤】  Python文件流缓冲机制_IO性能解析【教程】  html文件怎么打开证书错误_https协议的html打开提示不安全【指南】  Laravel的.env文件有什么用_Laravel环境变量配置与管理详解  php做exe能调用系统命令吗_执行cmd指令实现方式【详解】  laravel怎么通过契约(Contracts)编程_laravel契约(Contracts)编程方法  Laravel如何使用Passport实现OAuth2?(完整配置步骤)  Laravel怎么生成URL_Laravel路由命名与URL生成函数详解  海南网站制作公司有哪些,海口网是哪家的?  logo在线制作免费网站在线制作好吗,DW网页制作时,如何在网页标题前加上logo?  Android实现代码画虚线边框背景效果  javascript读取文本节点方法小结  如何在 Telegram Web View(iOS)中防止键盘遮挡底部输入框