glibcxx STL在std :: valarray :: sum()的实现中是否不正确?

fja*_*don 9 c++ gcc valarray

当我遇到一些我认为是编译器STL实现中的错误时,我正在玩valarrays.这是我可以生成的最小的例子:

#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
#include <valarray>

using namespace std;

int main()
{
    valarray<int> Y(0xf00d, 1);
    valarray<valarray<int>> X(Y, 1);
    cout << "Y[0]           = " << std::hex << Y[0]       << '\n';
    cout << "X[0][0]        = " << std::hex << X[0][0]    << '\n';
    cout << "X[0].size()    = " << X[0].size()            << '\n';
    cout << "X.sum().size() = " << X.sum().size()         << '\n';
}
Run Code Online (Sandbox Code Playgroud)

这将输出:

$ g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
Y[0]           = f00d
X[0][0]        = f00d
X[0].size()    = 1
X.sum().size() = 0
Run Code Online (Sandbox Code Playgroud)

您可以在coliru编译并运行它

为什么我认为这是一个错误?因为按照标准(26.6.2.8)

T sum()const;

此函数只能针对可应用operator + =的类型T进行实例化.此函数返回数组的所有元素的总和.如果数组的长度为0,则行为未定义.如果数组的长度为1,则sum()返回元素0的值.否则,通过将operator + =应用于数组元素的副本以及数组中所有其他元素的未指定顺序来计算返回值.

valarray确实有一个+ =运算符

所以我希望X.sum()有相同的价值X[0].但事实显然不是这样,因为它的大小是0而不是1.

我查看了实现sum()并将其追溯到这段代码:

  //
  // Compute the sum of elements in range [__f, __l)
  // This is a naive algorithm.  It suffers from cancelling.
  // In the future try to specialize
  // for _Tp = float, double, long double using a more accurate
  // algorithm.
  //
  template<typename _Tp>
    inline _Tp
    __valarray_sum(const _Tp* __f, const _Tp* __l)
    {
      _Tp __r = _Tp();
      while (__f != __l)
        __r += *__f++;
      return __r;
    }
Run Code Online (Sandbox Code Playgroud)

我们了解问题的来源.代码累加了总和__r,但不是__r用valarray中的第一个项初始化,而是默认构造.valarray的默认构造函数创建一个大小为0的数组.因此最终结果仍然是大小为0的valarray.

我对标准的理解是否有效(和glibcxx STL有错误)?或者我应该纠正?

为了记录,我在cygwin下使用g ++ 7.3.0,但它在coliru上复制,可能不在cygwin下运行...

Nat*_*ica 5

这对我来说是个错误. sum()

要求: size() > 0.该函数可以仅实例化一个类型T,其operator+=可以被应用.

valarray确实有operator +=如此之资格.它的operator +=

要求: size() == v.size().如果指示的运算符可以应用于两个类型为T的操作数,则每个运算符只能为类型T实例化.Valarray复合赋值运算符左侧的元素值不依赖于值.左手边的另一个元素.

因此,通过这样做_Tp __r = _Tp();,他们产生valarray,其size()不等于元件的尺寸因此它不能使用与它的operator +=.一个更正确的实现将是

  template<typename _Tp>
    inline _Tp
    __valarray_sum(const _Tp* __f, const _Tp* __l)
    {
      _Tp __r = *__f++; // this is okay as the function is requires size > 0.  It is the users responsibility to make sure that is the case
      while (__f != __l)
        __r += *__f++;
      return __r;
    }
Run Code Online (Sandbox Code Playgroud)