何时选用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
相关产品推荐
相关产品推荐

