Почему функция возврата std:: for_each (from, to, function) возвращается?
Я просто прочитал код для std::for_each
:
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f)
{
for ( ; first!=last; ++first ) f(*first);
return f;
}
и не смог найти веских причин для этой функции шаблона вернуть функцию ввода. У кого-нибудь есть примеры, где это было бы полезно?
Ответы
Ответ 1
Это позволит вам накапливать состояние в вашей функции, а затем возвращать его в код вызова. Например, ваша функция (как класс функтора) может иметь член int для подсчета количества раз, когда она была вызвана.
Вот страница с некоторыми примерами: http://xenon.arcticus.com/c-morsels-std-for-each-functors-member-variables
Ответ 2
Возможно, у Alex Stepanov была парадигма функционального программирования, но вы обнаружите, что оба 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() );
Вместо функтора вы можете использовать функцию (или в С++ 11 лямбда). Обратите внимание, что функтор здесь не имеет состояния, и вы передаете свой инициализированный объект в качестве параметра, который может быть временным.
Теперь мы знаем, что Y является скопируемым. std::accumulate
использует by value
в Y, а не на месте. Кстати, когда изменение на месте действительно более эффективно, существует обходной путь без написания нового алгоритма (например, accumulate2, который использует + = или модификацию ссылок) с использованием сигнатуры функции:
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)
, а не постоянным временем. Таким образом, если копия является довольно дорогостоящей, двойная копия в функторе не будет значительным издержками, итерируя небольшое количество раз над большими коллекциями, тогда как для накопления это будет (и предположение было бы ручкой указателя).
Ответ 3
Возвращение функции в основном делает std::for_each
посредственным подражанием std::accumulate
. Это позволяет вам накапливать что-то в функции/функторе, а затем извлекать это накопленное значение, когда это будет сделано. Почти каждый раз, когда вы думаете, что это может быть полезной задачей, вам, вероятно, следует рассмотреть возможность использования std::accumulate
.
Ответ 4
полезно, если вы хотите сохранить (и позже использовать) состояние функтора между вызовами, например, подсчитать количество элементов в коллекциях или указать некоторую неспособность обработать элемент, установив некоторую внутреннюю переменную.
Ответ 5
Если вы передаете объект функции, как функтор, и он имеет состояние, возвращающий объект функции, вы можете получить доступ к нему после повторения последовательности. Скажем, у вас есть объект функции, который вычисляет три разные переменные из последовательности и удерживает их в переменных-членах. Каждый раз, когда вызывается функтор, вы обновляете счетчики. Если for_each не вернул объект, как бы вы получили результат?
Примечание... поэтому вы всегда должны реализовывать построение копирования и назначение для объектов функций с состоянием.
Ответ 6
Никакой конкретной причины, я думаю. Однако вы можете использовать возвращенную функцию в другом вызове foreach, тем самым избегая писать имя функции дважды и, возможно, совершая ошибку там.