9

当我遇到我认为编译器的 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';
}

这将输出:

$ 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

您可以在coliru编译并运行它

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

T sum() 常量;

此函数只能为可以应用 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;
    }

我们知道问题出在哪里。代码将总和累加到__r中,但不是__r使用 valarray 中的第一项进行初始化,而是默认构造。valarray 的默认构造函数创建一个大小为 0 的数组。所以最终结果仍然是一个大小为 0 的 valarray。

我对标准的理解是否有效(并且 glibcxx STL 有错误)?还是我应该纠正?

作为记录,我在cygwin下使用g ++ 7.3.0,但它是在coliru上复制的,它可能没有在cygwin下运行......

4

1 回答 1

5

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

要求: size() > 0。此函数只能针对可以应用的类型T进行实例化。operator+=

并且valarray确实有一个operator +=所以它有资格。它是operator +=

要求: size() == v.size()。如果指定的运算符可以应用于 T 类型的两个操作数,则这些运算符中的每一个只能针对类型 T 实例化。 valarray 复合赋值运算符左侧元素的值不依赖于左侧的另一个元素。

因此,通过这样做_Tp __r = _Tp();,它们会生成不等于元素大小的 a ,因此它不能与 it's 一起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;
    }
于 2018-10-17T19:10:13.950 回答