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;
}