Подтвердить что ты не робот

Почему функция возврата 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;
}

и не смог найти веских причин для этой функции шаблона вернуть функцию ввода. У кого-нибудь есть примеры, где это было бы полезно?

4b9b3361

Ответ 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, тем самым избегая писать имя функции дважды и, возможно, совершая ошибку там.