Может быть, у Алекса Степанова была парадигма функционального программирования, но вы обнаружите, что и std::accumulate
, и std::for_each
передают свои операнды (функцию и накопленное значение) по значению, а не по ссылке. Таким образом:
class MyFunctor
{
Y val;
public:
MyFunctor() : val() {}
void operator()( X const& x )
{
// do something to modify val based on x
}
Y getValue() const { return val; }
};
Теперь, если вы попробуете:
MyFunctor f;
for_each( coll.begin(), coll.end(), f );
Y y = f.getValue();
Это не будет работать, потому что for_each
имел дело с копиями f
. Конечно, у вас может быть экземпляр shared_ptr<Y>
внутри, который будет указывать на тот же экземпляр. Вы также можете сделать val внутри MyFunctor ссылкой, создать его вне цикла и передать в MyFunctor.
Однако язык позволяет вам просто:
Y y = for_each( coll.begin(), coll.end(), MyFunctor() ).getValue();
красиво и удобно, все в одну строчку.
Чтобы сделать то же самое с std::accumulate
было бы сделано так:
class MyFunctor2
{
public:
Y operator()( Y y, X const& x ) const
{
// create a new Y based on the old one and x
...
}
};
Y y = std::accumulate( coll.begin(), coll.end(), Y(), MyFunctor2() );
Вы можете использовать функцию (или в C ++ 11 лямбду) вместо функтора. Обратите внимание, что у функтора нет состояния, и вы передаете инициализированный объект в качестве параметра, который может быть временным.
Теперь мы знаем, что Y можно копировать. std::accumulate
использует by value
для Y, а не модификацию на месте. Между прочим, когда модификация на месте действительно более эффективна, существует обходной путь без написания нового алгоритма (например, накопления2, который использует + = или ссылочную модификацию) с использованием сигнатуры функции:
Y * func( Y* py, X const & ); // function modifies *py in-place then returns py
затем звоните:
Y y;
std::accumulate( coll.begin(), coll.end(), &y, func );
Мы «знаем», что возвращаемое значение будет & y. Мы можем использовать это, если хотим получить доступ к члену Y в одном месте, например,
Y y;
Z z = std::accumulate( coll.begin(), coll.end(), &y, func )->getZ();
Кстати, ключевое отличие копии в for_each
и копии в accumulate
заключается в сложности / количестве копий, которые она сделает. С for_each
будет сделано не более 2 копий вашего функтора: одна в качестве параметра в функцию и одна в возвращаемом. Я говорю «максимум», потому что Оптимизация возвратного значения может уменьшить второй из этих копий. С accumulate
он копирует с каждым элементом в коллекции, т.е. O(N)
, а не с постоянным временем. Таким образом, если копия слегка дорогая, то двойная копия в функторе не будет большим расходом, повторяющимся небольшое количество раз по большим коллекциям, тогда как для накопления это будет (и предложение будет взломать указатель).