Цикл for_each
предназначен для того, чтобы скрыть итераторы (подробности о том, как реализован цикл) от пользовательского кода и определить четкую семантику операции: каждый элемент будет повторяться ровно один раз.
Проблема с удобочитаемостью в текущем стандарте состоит в том, что он требует функтора в качестве последнего аргумента вместо блока кода, поэтому во многих случаях вы должны написать для него определенный тип функтора. Это превращается в менее читаемый код, так как объекты функтора не могут быть определены на месте (локальные классы, определенные внутри функции, не могут использоваться в качестве аргументов шаблона), и реализация цикла должна быть удалена от фактического цикла.
struct myfunctor {
void operator()( int arg1 ) { code }
};
void apply( std::vector<int> const & v ) {
// code
std::for_each( v.begin(), v.end(), myfunctor() );
// more code
}
Обратите внимание, что если вы хотите выполнить определенную операцию с каждым объектом, вы можете использовать std::mem_fn
, или boost::bind
(std::bind
в следующем стандарте), или boost::lambda
(лямбда в следующем стандарте) для сделать это проще:
void function( int value );
void apply( std::vector<X> const & v ) {
// code
std::for_each( v.begin(), v.end(), boost::bind( function, _1 ) );
// code
}
Который не менее читабелен и более компактен, чем версия, свернутая вручную, если у вас есть функция / метод для вызова на месте. Реализация может предоставить другие реализации цикла for_each
(подумайте о параллельной обработке).
Будущий стандарт по-разному устраняет некоторые недостатки, он допускает использование локально определенных классов в качестве аргументов для шаблонов:
void apply( std::vector<int> const & v ) {
// code
struct myfunctor {
void operator()( int ) { code }
};
std::for_each( v.begin(), v.end(), myfunctor() );
// code
}
Улучшение локализации кода: когда вы просматриваете, вы видите, что он делает прямо там. На самом деле вам даже не нужно использовать синтаксис класса для определения функтора, но используйте лямбду прямо здесь:
void apply( std::vector<int> const & v ) {
// code
std::for_each( v.begin(), v.end(),
[]( int ) { // code } );
// code
}
Даже если для случая for_each
найдется специальная конструкция, которая сделает его более естественным:
void apply( std::vector<int> const & v ) {
// code
for ( int i : v ) {
// code
}
// code
}
Я склонен смешивать конструкцию for_each
с петлями, скрученными вручную. Когда мне нужен только вызов существующей функции или метода (for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )
), я обращаюсь к конструкции for_each
, которая убирает из кода много вещей итератора. Когда мне нужно что-то более сложное, и я не могу реализовать функтор на пару строк выше фактического использования, я запускаю свой собственный цикл (сохраняет операцию на месте). В некритических разделах кода я мог бы пойти с BOOST_FOREACH (коллега включил меня в это)