You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

何时选用std::expected而非C++异常?结合代码示例解析

std::expected vs 异常:该怎么选?

适用场景区分

  • 适合用异常的情况

    • 错误属于罕见、意外事件:比如内存分配失败、数组越界这类正常流程几乎碰不到的问题,用异常能让核心逻辑代码更清爽,不用在每个步骤都处理错误返回值。
    • 错误需要跨多层调用栈传递:如果错误得从底层函数传到上层调用者,异常会自动完成栈展开,不用手动在每个函数层级传递错误码,代码更简洁。
    • 贴合C++标准库风格:标准容器、字符串操作的错误处理大多用异常,保持一致能减少团队协作时的理解成本。
  • 适合用std::expected的情况

    • 错误是预期内的常见情况:比如字符串转整数时的格式错误、数值溢出,这类错误是函数设计时就明确可能发生的,调用者通常需要第一时间处理。
    • 需要明确区分错误类型:像你提到的parse_int,用自定义parse_error枚举(比如invalid_format、out_of_range)能让调用者一眼看懂错误原因,比捕获不同异常类型更直接。
    • 环境不允许用异常:比如实时系统、嵌入式设备,异常的栈展开可能带来不可控的性能波动,或者项目编译时禁用了异常(-fno-exceptions)。
    • 链式错误处理更顺手:std::expected支持and_then、or_else这类链式操作,能以更简洁的声明式风格处理连续的可能失败的操作,避免嵌套一堆if-else。

客观技术因素(不止是风格偏好)

性能

  • 异常:没抛出异常时,零成本模型让正常路径几乎没性能损耗;但一旦抛出异常,栈展开、异常对象构造会带来明显开销,所以只适合错误很少发生的场景。
  • std::expected:正常路径需要返回包含值或错误的联合体(内部一般用std::variant实现),会有少量内存和构造开销;错误路径的开销和正常路径差不多,适合错误频繁出现的场景。

代码体积与编译速度

  • 异常:编译器要生成额外的栈展开信息(EH frame),会增大二进制体积;同时异常处理的代码生成逻辑更复杂,可能拖慢编译速度。如果项目禁用异常,这部分开销就没了。
  • std::expected:模板实例化会带来一定的代码膨胀,但比异常的EH frame开销更可控;编译时不用处理异常相关的复杂逻辑,大型项目里编译速度可能更快。

ABI兼容性

  • 异常:不同编译器(甚至同一编译器的不同版本)的异常处理ABI可能不一致,跨模块调用时如果抛出异常,容易出现未定义行为。
  • std::expected:作为C20标准模板,只要编译器支持C20,其ABI是稳定的,跨模块调用更安全,尤其适合动态链接库的场景。

代码示例对比

异常实现的parse_int

#include <stdexcept>
#include <string>
#include <limits>

int parse_int(const std::string& s) {
    size_t pos;
    long val = std::stol(s, &pos);
    if (pos != s.size()) {
        throw std::invalid_argument("invalid integer format");
    }
    if (val < std::numeric_limits<int>::min() || val > std::numeric_limits<int>::max()) {
        throw std::out_of_range("integer out of range");
    }
    return static_cast<int>(val);
}

std::expected实现的parse_int

#include <expected>
#include <string>
#include <limits>

enum class parse_error {
    invalid_format,
    out_of_range
};

std::expected<int, parse_error> parse_int(const std::string& s) {
    size_t pos;
    long val = std::stol(s, &pos);
    if (pos != s.size()) {
        return std::unexpected(parse_error::invalid_format);
    }
    if (val < std::numeric_limits<int>::min() || val > std::numeric_limits<int>::max()) {
        return std::unexpected(parse_error::out_of_range);
    }
    return static_cast<int>(val);
}

内容的提问来源于stack exchange,提问作者Jan Schultke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 18:52:58