On-line: гостей 0. Всего: 0 [подробнее..]
Программисты всех стран, объединяйтесь!

АвторСообщение



ссылка на сообщение  Отправлено: 01.02.15 23:11. Заголовок: MS VC++ 2014: a std::accumulate's bug.


Реализация алгоритма std::accumulate в MS VC++ содержит неочевидный баг. Он неочевидный по той причине, что редко кто передает аргумент для параметра алгоритма init по ссылке, хотя это не воспрещается делать и на самом деле бывает очень удобно и эффективно с точки зрения работы алгоритма, так как не создаются временные объекты.

Я думаю, что этот баг имеет место во всех версиях компилятора MS VC++. По крайней мере он присутствует в онлайновой версии компилятора от 2014 года, которая маркируется как
Compiler version: 19.00.22318(x86) Last updated: Nov

Вот программа, которая демонстрирует этот баг:
 
#include <iostream>
#include <numeric>
#include <iterator>

int main()
{
int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

for ( int x : a ) std::cout << x << ' ';
std::cout << std::endl;

long long int acc = 0;
std::cout << std::accumulate<decltype( std::begin( a ) ), long long int &>
( std::begin( a ), std::end( a ), acc ) << std::endl;

std::cout << acc << std::endl;

return 0;
}

Здесь параметр init, который инициализируется аргументом acc явно указан, как имеющий тип ссылки long long int &. Поэтому объект acc после работы алгоритма должен быть равен сумме элементов массива a.

Однако данный код не компилируется компилятором MS VC++ из-за того, что реализация этого алгоритма в MS VC++ выполнена таким образом, что он вызывает другую внутреннюю функцию, где уже этот аргумент передается не по ссылке. Из-за этого и возникает ошибка компиляции.

Эта же программа успешно компилируется как компилятором GCC, так и компилятором Clang.

Я отослал сообщение о дефекте в Майкрософт, но последнее время там как-то вяло реагируют на подобные сообщения. То есть они остаются без ответа.



Спасибо: 0 
ПрофильЦитата Ответить
Ответов - 3 [только новые]





ссылка на сообщение  Отправлено: 10.02.15 11:22. Заголовок: Точно такая же пробл..


Точно такая же проблема существует с реализацией алгоритма std::inner_product в MS VC++. То есть следующая программа не будет компилироваться:
 
#include <iostream>
#include <numeric>
#include <iterator>

int main()
{
int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int b[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };

for ( int x : a ) std::cout << x << ' ';
std::cout << std::endl;

for ( int x : b ) std::cout << x << ' ';
std::cout << std::endl;

std::cout << std::endl;

long long int acc = 0;

std::cout << std::inner_product<decltype( std::begin( a ) ),
decltype( std::begin( b ) ),
long long int &>
( std::begin( a ), std::end( a ), std::begin( b ), acc ) << std::endl;

std::cout << acc << std::endl;

return 0;
}

Хотя если запустить эту программу на www.ideone.com, используя компилятор GCC, или если запустить программу, используя clang 3.4, то она успешно скомпилируется и выведет на консоль следующий результат:
 
0 1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1 0

120
120



Спасибо: 0 
ПрофильЦитата Ответить



ссылка на сообщение  Отправлено: 11.02.15 08:33. Заголовок: Можно говорить про б..


Можно говорить про баги в этих алгоритмах при условии, что аккумулятор внутри них имеет тот же тип, что и инициализатор init.
Однако если почитать стандарт C++, то окажется, что нигде в описании этих алгоритмов не говорится о том, какой тип будет иметь аккумулятор внутри алгоритмов. Вот, например, пункт из стандарта C++,
где говорится о принципе работы алгоритма std::accumulate:


 цитата:
1 Effects: Computes its result by initializing the accumulator acc with the initial value init and then modifies it with acc = acc + *i or acc = binary_op(acc, *i) for every iterator i in the range [first,last) in order.



Конечно подразумевается, что тип аккумулятора точно такой же, как тип инициализатора init, иначе алгоритмы будут иметь неопределенное поведение. Представьте для примера, когда инициализатор при вызове std::accumulate имеет тип double, в то время как аккумулятор внутри алгоритма определяется как имеющий тип int. В этом случае результат работы алгоритма может быть не тем, что вы ожидаете.

Очевидно, что в описании в стандарте C++ этих алгоритмов, std::accumulate и std::inner_product, следует явно указать, какой тип будет у аккумулятора, иначе это выглядит как дефект стандарта.

Поэтому соответствующее предложение по исправлению описания этих алгоритмов в стандарте C++ я направлю в Комитет по стандартизации..


Спасибо: 0 
ПрофильЦитата Ответить



ссылка на сообщение  Отправлено: 11.02.15 15:57. Заголовок: Чтобы было наглядно ..


Чтобы было наглядно видно, как определение внутреннего аккумулятора алгоритмов влияет на их поведение, можно рассмотреть следующий пример.

Если, например, алгоритм std::accumulate определить следующим образом
 
template <class InputIterator, class T>
T accumulate( InputIterator first, InputIterator last, T init )
{
T acc = init;

for ( ; first != last; ++first ) acc = acc + *first;

return acc;
}

то нижеприведенная программа будет успешно компилироваться
 
#include <iostream>
#include <iterator>

template <class InputIterator, class T>
T accumulate( InputIterator first, InputIterator last, T init )
{
T acc = init;

for ( ; first != last; ++first ) acc = acc + *first;

return acc;
}

int main()
{
int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

long long acc = 0;

accumulate<decltype( std::begin( a ) ), long long &>
( std::begin( a ), std::end( a ), acc );

std::cout << acc << std::endl;

return 0;
}

и выведет на консоль значение 45.

Ежели определить этот алгоритм таким образом, как
 
template <class InputIterator, class T>
T accumulate( InputIterator first, InputIterator last, T init )
{
auto acc = init;

for ( ; first != last; ++first ) acc = acc + *first;

return acc;
}

то данная программа (необходимо в нее подставить новое определение алгоритма вместо предыдущего) уже не выдаст ожидаемый результат.

Так что очень важно, как аккумуляторы определяются внутри этих двух алгоритмов: std::accumulate и std::inner_product.


Спасибо: 0 
ПрофильЦитата Ответить
Ответ:
1 2 3 4 5 6 7 8 9
большой шрифт малый шрифт надстрочный подстрочный заголовок большой заголовок видео с youtube.com картинка из интернета картинка с компьютера ссылка файл с компьютера русская клавиатура транслитератор  цитата  кавычки моноширинный шрифт моноширинный шрифт горизонтальная линия отступ точка LI бегущая строка оффтопик свернутый текст

показывать это сообщение только модераторам
не делать ссылки активными
Имя, пароль:      зарегистрироваться    
Тему читают:
- участник сейчас на форуме
- участник вне форума
Все даты в формате GMT  3 час. Хитов сегодня: 13
Права: смайлы да, картинки да, шрифты да, голосования нет
аватары да, автозамена ссылок вкл, премодерация откл, правка нет