STL glibcxx неверен в своей реализации std::valarray::sum()?

Я играл с valarrays, когда столкнулся с чем-то, что я считаю ошибкой в ​​реализации STL моего компилятора. Вот самый маленький пример, который я мог привести:

#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 () const;

Эта функция может быть создана только для типа T, к которому может быть применен оператор +=. Эта функция возвращает сумму всех элементов массива. Если массив имеет длину 0, поведение не определено. Если массив имеет длину 1, sum() возвращает значение элемента 0. В противном случае возвращаемое значение вычисляется путем применения оператора += к копии элемента массива и всех других элементов массива в неопределенном порядке.

У 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. Таким образом, конечный результат по-прежнему остается valarray размера 0.

Верно ли мое понимание стандарта (и в glibcxx STL есть ошибка)? Или я должен быть исправлен?

Для записи я использую g++ 7.3.0 под Cygwin, но он воспроизводится на coliru, который, вероятно, не работает под Cygwin...

1 ответ

Решение

Это ошибка для меня. 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;
    }
Другие вопросы по тегам