std::accumulate 可以扔吗?

can std::accumulate throw?

std::accumulate 的 c++ 参考没有提到 std::accumulate 可能抛出的任何异常,它的定义仍然不包含 noexcept。假设一个人使用不抛出的类型和操作,在声明为 noexcept 的成员函数中使用 std::accumulate 是安全的,还是在 UB 中招致一个?

例如:

#include <iostream>
#include <numeric>
#include <vector>

class A {
  std::vector<int> m_v;
public:
  A(std::size_t N) : m_v(N, 1) { }
  int sum() const noexcept { return std::accumulate(m_v.begin(), m_v.end(), 0); }
};

int main()
{
     A x{3};
     std::cout << x.sum() << std::endl;
     return 0;
}

是否将 A::sum() 声明为 noexcept UB 来源?

是的,一般情况下可以。

首先,可通过模板参数自定义且 std::accumulate 必须执行的所有操作都可以抛出。

但即使除此之外,如果标准库函数没有非抛出异常规范并且函数的描述没有另外说明,则标准确实允许实现抛出实现定义的异常,请参阅[res.on.exception.handling]/4.

也就是说,如果在您的示例中使用 std::accumulate 会抛出异常,我会感到惊讶。不需要动态分配,因此不需要潜在的 std::bad_alloc 抛出,这是实现定义的异常最有可能的候选者。 int 上的求和、复制 and/or 移动操作也不会抛出。

在任何情况下,如果函数内部抛出异常,将 noexcept 添加到函数中不会导致未定义的行为。相反,明确定义的是,如果异常到达 noexcept 函数的外部范围,将调用 std::terminate,默认情况下会中止程序,但可以在某种程度上进行自定义。

std::accumulate() 有先决条件,例如,范围的末尾可以从范围的开始到达。到目前为止,标准库还没有将 noexcept 放在有前提条件的函数上(至少,一般情况下没有;可能有特殊情况),因为调试实现可以​​断言存在问题,例如,通过抛出异常: 当未定义的行为被触发时会发生什么是未定义的,如果他们愿意的话,实现可以自由定义任何一个。

此外,std::accumulate() 调用的任何函数都允许抛出异常,即任何 noexcept 声明都需要有条件。我认为算法一般不太可能获得相应的 noexcept 声明。

由于规范没有提到在合约中调用时 std::accumulate() 抛出的任何情况,如果被调用操作的 none 抛出则不会抛出。