STL glibcxx неверен в своей реализации std :: valarray :: sum ()? - PullRequest
0 голосов
/ 17 октября 2018

Я играл с 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 Ответ

0 голосов
/ 17 октября 2018

Это ошибка для меня.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;
    }
...