【问题标题】:Should a theoretically, but not practically, throwing function be declared noexcept?理论上但不是实际上的抛出函数应该声明为 noexcept 吗?
【发布时间】:2015-10-26 20:41:18
【问题描述】:

即使 v.at(idx) 理论上可以抛出 out_of_range 异常,但实际上不是由于边界检查,声明以下函数 noexcept 是否安全?

int get_value_or_default(const std::vector<int>& v, size_t idx) noexcept {
    if (idx >= v.size()) {
        return -1;
    }
    return v.at(idx);
}

【问题讨论】:

  • 加一个,但这不一定安全。如果另一个线程修改了v,会发生什么?
  • @Bathsheba,那么代码已经具有未定义的行为,因为它调用v.size()v.at(idx) 没有同步,所以担心这些事情是没有意义的。要么没有发生,要么程序有更大的问题。
  • 此代码不是线程安全的。但是,这个问题似乎只针对单线程情况。
  • 你也可以返回v[idx] - 没有必要检查两次范围
  • @GuyRT 可能是为了示例

标签: c++ function noexcept


【解决方案1】:

您对“安全”的定义是什么?如果您在标记为noexceptnoexcept(true) 的函数中抛出异常,那么您的程序将被终止(标准 15.4.9)

每当抛出异常并且对处理程序 (15.3) 的搜索遇到 具有不允许异常的异常规范的函数,那么,

  • 如果异常规范是动态异常规范,则函数 std::unexpected() 是 称为 (15.5.2),
  • 否则,将调用函数 std::terminate() (15.5.1)。

只要这是可以接受的,那么你就可以了。如果您不能容忍程序终止,那么它是不安全的。

【讨论】:

  • 关键是永远不会抛出异常,所以即使程序不能容忍终止,它仍然是安全的,因为它不会调用 std::terminate()。
  • @JonathanWakely 你能保证这个函数永远不会抛出异常吗?由于该函数不是线程安全的,因此向量可以被另一个线程修改,并且在边界检查和检索之间大小会发生变化。
  • @NathanOliver:正如乔纳森在其他地方指出的那样,如果这些访问是不同步的,那么你会遇到更大的问题,这会掩盖你在这个函数中可能做的任何事情(除了引入同步)。这样的努力实际上是毫无意义的。
【解决方案2】:

这是安全的。如果发生异常,声明函数noexcept 将导致程序立即终止(通过调用terminate())。只要不抛出异常,一切都很好。

【讨论】:

    【解决方案3】:

    我们没有完整的场景来确定准确该函数是否能够抛出。


    尽管您的假设是正确的:由于您正在做std::vector::at 无论如何都会做的事情,因此在给定的上下文中不太可能有害。但是,一般来说,此类函数可能会受到无效因素的影响,因此可能会引发异常。这些可能是线程、进程或信号,它们可以将执行与可能修改std::vector 的代码交错,这些代码无法安全处理,std::vector::at 也不能。

    如果你也想考虑这些可能性,你需要有类似的东西

    int get_value_or_default(const std::vector<int>& v, size_t idx) noexcept       
    {
        try { return v.at(idx); }
        catch (std::out_of_range const& e) { return -1; }
    }
    

    【讨论】:

    • 正如乔纳森在其他地方指出的那样,如果这些访问是不同步的,那么你会遇到更大的问题,这会掩盖你在这个函数中可能做的任何事情(除了引入同步)。这样的努力实际上是毫无意义的。
    • @LightnessRacesinOrbit 确实如此,但严格取决于整体和实际代码。不可能有可以消除这种担忧的异步访问。我试图关注代码的noexceptness,因为这是问题的重点,异步效果可以做什么肯定是超出的。
    • 对,鉴于此,noexcept 没有问题。
    【解决方案4】:

    即使v.at(idx) 理论上可以抛出out_of_range 异常,但实际上不是由于边界检查?

    理论上是不可能的。

    如果您从理论上考虑该函数,那么边界检查是您必须考虑的关于该函数作为一个整体的理论的一部分,因此它可以抛出的唯一理论方法是是否存在条件idx &gt;= v.size() 不是真的,但 v.at(idx) 扔了。

    所以在你说它“理论上可以抛出”是错误的。

    (实际上它可能会抛出,如果你在使用 at() 的实现中有一个错误,但是你会遇到更大的问题并且因为noexcept 可能会导致你这样做可能会更好)。

    【讨论】:

      【解决方案5】:

      可能是 big-O 组织中的某个人做得很聪明,并从 std::vector&lt;int&gt; 派生了他的类,然后重载了 size() 和 at() 以使其符合自己的喜好。然后在代码中的某个地方调用了你的函数,但令所有人惊讶的是,程序终止了,而不是异常被更高层捕获。

      确实有点牵强,但这不仅仅是关于 std::vector&lt;int&gt; ... 的问题,而是关于背后的编程实践的问题。

      不幸的是,您对自己的输入做出了无法保证的假设,这就是它不安全的原因;至少在大型代码域中。

      【讨论】:

        猜你喜欢
        • 2020-10-24
        • 1970-01-01
        • 2013-08-22
        • 1970-01-01
        • 1970-01-01
        • 2014-03-05
        • 1970-01-01
        • 1970-01-01
        • 2014-05-07
        相关资源
        最近更新 更多