何时使用 std::expected 而不是异常

问题描述 投票:0回答:1

什么时候应该使用

std::expected
以及什么时候应该使用异常?以这个函数为例:

int parse_int(std::string_view str) {
    if (str.empty()) {
        throw std::invalid_argument("string must not be empty");
    }
    /* ... */
    if (/* result too large */) {
        throw std::out_of_range("value exceeds maximum for int");
    }
    return result;
}

我想在使用这个函数时区分不同的错误,所以我可以抛出不同类型的异常,这很有用。不过,我也可以用

std::expected
来做到这一点:

enum class parse_error {
    empty_string,
    invalid_format,
    out_of_range
};

std::expected<int, parse_error> parse_int(std::string_view str) noexcept {
    if (str.empty()) {
        return std::unexpected(parse_error::empty_string);
    }
    /* ... */
    if (/* result too large */) {
        return std::unexpected(parse_error::out_of_range);
    }
    return result;
}

是否有任何理由在例外情况(性能、代码大小、编译速度、ABI)上使用

std::expected
,或者只是风格偏好?

c++ exception error-handling c++23 std-expected
1个回答
0
投票

首先,无论您计划使用什么错误处理策略 - 在给定项目的一开始就建立它 - 请参阅 E.1:在设计早期制定错误处理策略。因为“稍后”改变这一策略的想法很可能会导致有两种策略——旧的和新的。

有时,选择很简单 - 当出于某种原因在给定项目中不允许出现异常时 - 只需使用

std::expected

提出一种适合所有需求的错误处理策略确实很难(我想说,不可能)。我只能在这里提出我尝试遵循的一项建议:


可能的错误处理策略之一,可以称为遵循名称

  1. 对于
    特殊、罕见、意外的情况使用exceptions。当 throw 指令真正被调用的可能性很低时。
  2. 对于
    预期的错误使用std::expected

有时,这可能意味着这两种方式都在一个函数中使用 - 就像函数返回

std::excpected<T, E>
一样,这是预期的
E
错误,但该函数没有标记为
noexcept
因为它可能会在一些非常罕见的情况下抛出。但是,如果您建立的错误策略是返回
std::expected<T,E>
的函数永远不会抛出 - 那么您需要让这个“意外”错误成为
E
的变体。

© www.soinside.com 2019 - 2024. All rights reserved.