Преимущества цикла std :: for_each over for

168

Существует ли какое - либо преимущество std::for_eachнад forциклом? Мне std::for_eachтолько кажется, что мешает удобочитаемости кода. Почему тогда некоторые стандарты кодирования рекомендуют его использовать?

missingfaktor
источник
10
std::for_eachпри использовании с boost.lambdaили boost.bindчасто может улучшить читаемость
Вопрос и принятый ответ с 2010 года. Более актуальный ответ (с 2018 года) см. Здесь: fluentcpp.com/2018/03/30/is-stdfor_each-obsolete
Сегал-Халеви

Ответы:

181

С C ++ 11 (ранее называвшимся C ++ 0x) приятно то , что эта утомительная дискуссия будет решена.

Я имею в виду, никто в здравом уме, кто хочет перебрать всю коллекцию, все равно будет использовать это

for(auto it = collection.begin(); it != collection.end() ; ++it)
{
   foo(*it);
}

Или это

for_each(collection.begin(), collection.end(), [](Element& e)
{
   foo(e);
});

когда доступен синтаксис цикла на основе диапазонаfor :

for(Element& e : collection)
{
   foo(e);
}

Этот вид синтаксиса был доступен в Java и C # в течение некоторого времени, и на самом деле в каждом недавнем коде Java или C #, который я видел, имеется гораздо больше foreachциклов, чем классических forциклов.

Томас Пети
источник
12
На самом деле цикл foreach с scoop долгое время был доступен в boost, и я все еще хочу повторить с for_each и лямбда-функцией.
Виктор Сер
19
Предположение о желании получить весь диапазон контейнеров не является частью вопроса, так что это только частичный ответ.
Адриан Маккарти
6
Обратите внимание, что зацикливание на элементе, вероятно, не единственное, что вы хотите сделать, поэтому может быть хорошей идеей использовать for_each, просто чтобы вы узнали о find / partition / copy_replace_if и других, что на самом деле очень много для циклов делать.
Мак
10
Range-for хорош, за исключением случаев, когда вам действительно нужен итератор (тогда нет способа добраться до него).
Дэймон
5
Я бы не использовал, даже Element & eесли auto & e(или auto const &e) выглядит лучше. Я бы использовал Element const e(без ссылки), когда я хочу неявное преобразование, скажем, когда источник представляет собой коллекцию разных типов, и я хочу, чтобы они преобразовывались в Element.
Наваз
54

Вот несколько причин:

  1. Кажется, это затрудняет читабельность только потому, что вы к этому не привыкли и / или не используете правильные инструменты, чтобы сделать его действительно простым. (см. boost :: range и boost :: bind / boost :: lambda для помощников. Многие из них войдут в C ++ 0x и сделают for_each и связанные функции более полезными.)

  2. Это позволяет вам написать алгоритм поверх for_each, который работает с любым итератором.

  3. Это уменьшает вероятность глупых ошибок ввода.

  4. Она также открывает свой ум к остальной части STL-алгоритмов, как find_if, sort, replaceи т.д. , и это не будет выглядеть так странно больше. Это может быть огромной победой.

Обновление 1:

Самое главное, это поможет вам выйти за рамки for_eachцикла for, как будто это все, что есть, и посмотреть на другие STL-файлы, такие как find / sort / partition / copy_replace_if, параллельное выполнение ... или что-то еще.

Большая обработка может быть написана очень кратко, используя «остальную часть» братьев и сестер for_each, но если все, что вы делаете, это пишете цикл for с различной внутренней логикой, то вы никогда не научитесь использовать их, и вы в конечном итоге изобретать колесо снова и снова.

И (скоро будет доступен диапазон в стиле for_each):

for_each(monsters, boost::mem_fn(&Monster::think));

Или с C ++ x11 лямбдами:

for_each(monsters, [](Monster& m) { m.think(); });

ИМО более читабельно, чем:

for(Monsters::iterator i = monsters.begin(); i != monsters.end(); ++i) {
    i->think();
} 

Также это (или с лямбдами, см. Другие):

for_each(bananas, boost::bind(&Monkey::eat, my_monkey, _1));

Является более кратким, чем:

for(Bananas::iterator i = bananas.begin(); i != bananas.end(); ++i) {
    my_monkey->eat(*i);
} 

Особенно, если у вас есть несколько функций для вызова по порядку ... но, возможно, это только я. ;)

Обновление 2 : я написал свои собственные однострочные оболочки stl-algos, которые работают с диапазонами вместо пары итераторов. Однажды выпущенный boost :: range_ex будет включать его, и, возможно, он будет и в C ++ 0x?

Маке
источник
+1, несколько функций или вложенных типов: outer_class::inner_class::iteratorили они являются шаблонными аргументами: typename std::vector<T>::iterator... сама конструкция for может натолкнуться на
многострочную
4
(кстати: for_eachво втором примере неверно (должно бытьfor_each( bananas.begin(), bananas.end(),...
Дэвид Родригес - dribeas
Я написал обертки, которые используют диапазоны вместо двух итераторов. Они будут доступны позже (см. Range_ex), но у всех должны быть они в любом случае. (Добавлено обновление об этом.)
Macke
1
Поддержка параллельной обработки отсутствует. 1 причина здесь. Мы можем добавить реализацию, чтобы использовать cuda / gpu для гетерогенно-параллельных вычислений.
Шува
25

for_eachявляется более общим. Вы можете использовать его для перебора любого типа контейнера (передавая итераторы начала / конца). Вы можете поменять контейнеры под функцией, которая for_eachне требует обновления кода итерации. Вам нужно учитывать, что в мире есть и другие контейнеры, кроме std::vectorобычных C-массивов, чтобы увидеть преимущества for_each.

Основным недостатком for_eachявляется то, что требуется функтор, поэтому синтаксис неуклюжий. Это исправлено в C ++ 11 (ранее C ++ 0x) с введением лямбд:

std::vector<int> container;
...
std::for_each(container.begin(), container.end(), [](int& i){
    i+= 10;
});

Это не будет выглядеть странно для вас через 3 года.

Терри Махаффи
источник
3
+1. Объявление, когда for_each берет контейнер / диапазон вместо двух итераторов, становится еще более привлекательным.
Маке
2
@Marcus: это будет конструкция дальнего боя, и синтаксис не будет читать «for_each» сам по себе: for ( int v : int_vector ) {(даже если сегодня его можно смоделировать с помощью BOOST_FOREACH)
Дэвид Родригес - dribeas
@ Дэвид: я имею в виду общее добавление функций, основанных на диапазонах (так что вы можете использовать диапазоны со всеми этими функциями for_each, copy, remove_if и т. Д. И т. Д.),
Macke
1
Почему это невозможно написать: std::for_each(container, [](int& i){ ... });. Я имею в виду, почему один вынужден писать контейнер дважды?
Джорджио
1
@freitass: запись контейнера один раз, как в моем предыдущем комментарии, может по умолчанию использовать начальный итератор конца без явного вызова их. Большинство языков, которые предоставляют функции более высокого порядка для коллекций (Ruby, Scala, ...), пишут что-то вроде container.each { ... }без упоминания начальных и конечных итераторов. Я считаю излишним, что мне приходится все время указывать конечный итератор.
Джорджио
18

Лично, в любое время, когда мне нужно было бы выйти из-под контроля std::for_each(написать функторы специального назначения / сложные boost::lambda), я нахожу BOOST_FOREACHи диапазон C ++ 0x для более понятного:

BOOST_FOREACH(Monster* m, monsters) {
     if (m->has_plan()) 
         m->act();
}

против

std::for_each(monsters.begin(), monsters.end(), 
  if_then(bind(&Monster::has_plan, _1), 
    bind(&Monster::act, _1)));
Дядя Бен
источник
12

Это очень субъективно, некоторые скажут, что использование for_each сделает код более читабельным, так как позволяет обрабатывать разные коллекции с одинаковыми соглашениями. for_eachего реализация реализована в виде цикла

template<class InputIterator, class Function>
  Function for_each(InputIterator first, InputIterator last, Function f)
  {
    for ( ; first!=last; ++first ) f(*first);
    return f;
  }

так что вам решать, что подходит именно вам.

Alon
источник
11

Как и многие функции алгоритма, первоначальная реакция заключается в том, что думать, что использовать foreach более нечитаемо, чем цикл. Это была тема многих огненных войн.

Как только вы привыкнете к идиоме, вы можете найти ее полезной. Одно очевидное преимущество заключается в том, что он заставляет кодировщика отделить внутреннее содержимое цикла от фактической функциональности итерации. (Хорошо, я думаю, что это преимущество. Другие говорят, что вы просто нарезаете код без реальной выгоды).

Еще одно преимущество заключается в том, что когда я вижу foreach, я знаю, что либо каждый элемент будет обработан, либо будет сгенерировано исключение.

Цикл for позволяет несколько вариантов завершения цикла. Вы можете позволить циклу пройти полный курс, или вы можете использовать ключевое слово break, чтобы явно выпрыгнуть из цикла, или использовать ключевое слово return, чтобы выйти из всего промежуточного цикла функции. В отличие от foreach не позволяет использовать эти параметры, что делает его более читабельным. Вы можете просто взглянуть на имя функции и узнать всю природу итерации.

Вот пример запутанной для цикла:

for(std::vector<widget>::iterator i = v.begin(); i != v.end(); ++i)
{
   /////////////////////////////////////////////////////////////////////
   // Imagine a page of code here by programmers who don't refactor
   ///////////////////////////////////////////////////////////////////////
   if(widget->Cost < calculatedAmountSofar)
   {
        break;
   }
   ////////////////////////////////////////////////////////////////////////
   // And then some more code added by a stressed out juniour developer
   // *#&$*)#$&#(#)$#(*$&#(&*^$#(*$#)($*#(&$^#($*&#)$(#&*$&#*$#*)$(#*
   /////////////////////////////////////////////////////////////////////////
   for(std::vector<widgetPart>::iterator ip = widget.GetParts().begin(); ip != widget.GetParts().end(); ++ip)
   {
      if(ip->IsBroken())
      {
         return false;
      }
   }
}
Андрей Шепард
источник
1
Вы делаете хорошее замечание, но ваш пример мотивации не совсем справедлив. При использовании std::for_each()в старом стандарте (который использовался во время публикации) вы должны использовать именованный функтор, который, как вы говорите, обеспечивает удобочитаемость и запрещает преждевременное прерывание цикла. Но тогда эквивалентный forцикл не имеет ничего, кроме вызова функции, и это также запрещает преждевременное прерывание. Но кроме этого , я думаю , что вы сделали отличную точку, говоря , что std::for_each() навязывает , проходящие через весь диапазон.
wilhelmtell
10

Вы в основном правы: большую часть времени std::for_eachэто чистый убыток. Я бы пошел так далеко, чтобы сравнить for_eachс goto. gotoобеспечивает максимально универсальное управление потоком данных - вы можете использовать его для реализации практически любой другой структуры управления, которую только можете себе представить. Однако именно эта универсальность означает, что видение gotoизолированно практически ничего не говорит о том, что он должен делать в этой ситуации. В результате почти никто в здравом уме не используетgoto кроме как в крайнем случае.

Среди стандартных алгоритмов for_eachпрактически такой же способ - его можно использовать для реализации практически всего, что означает, что видение for_eachпрактически ничего не говорит о том, для чего он используется в этой ситуации. К сожалению, отношение людей к тому, for_eachгде их отношение gotoбыло, скажем, в 1970 году или около того - несколько человек поняли, что его следует использовать только в качестве крайней меры, но многие все еще считают его основным алгоритмом, и редко, если когда-либо использовать любой другой. Подавляющее большинство времени, даже быстрый взгляд показал бы, что одна из альтернатив была значительно выше.

Например, я почти уверен, что потерял счет, сколько раз я видел людей, пишущих код для распечатки содержимого коллекции с использованием for_each. Судя по сообщениям, которые я видел, это может быть наиболее распространенное использование for_each. В итоге они получают что-то вроде:

class XXX { 
// ...
public:
     std::ostream &print(std::ostream &os) { return os << "my data\n"; }
};

И их пост спрашивает о том, какой комбинации bind1st, mem_funи т.д. они должны сделать что - то вроде:

std::vector<XXX> coll;

std::for_each(coll.begin(), coll.end(), XXX::print);

работать, и распечатать элементы coll. Если бы это действительно работало именно так, как я написал там, это было бы посредственно, но это не так - и к тому времени, когда вы заставили это работать, трудно найти те несколько кусочков кода, связанных с тем, что происходит среди частей, которые держат его вместе.

К счастью, есть гораздо лучший способ. Добавьте обычную перегрузку потоковой вставки для XXX:

std::ostream &operator<<(std::ostream *os, XXX const &x) { 
   return x.print(os);
}

и использовать std::copy:

std::copy(coll.begin(), coll.end(), std::ostream_iterator<XXX>(std::cout, "\n"));

Это работает - и практически не требует никакой работы, чтобы выяснить, что печатает содержимое collв std::cout.

Джерри Гроб
источник
+1, есть одна ошибка, хотя В первом примере это должно быть, boost::mem_fn(&XXX::print)а неXXX::print
фактор
Вот почему я сказал, что этот пример не сработает, и они публикуют просьбу о помощи, чтобы заставить его работать (о, и вам также нужно связать в std::coutкачестве аргумента, чтобы он работал).
Джерри Коффин
1
Хотя это, как правило, хороший ответ, вопрос не в значении for_each или его значении по сравнению с другими алгоритмами std, а в его значении по сравнению с циклом for. Вы можете подумать об этом в тех случаях, когда другой стандартный алгоритм не применяется. Тогда вы бы использовали цикл for_each или for? Подумайте об этом, и что бы вы ни придумали, это должен был быть ваш ответ.
Кристиан Рау
@ChristianRau: всегда есть тонкая грань между ответом на вопрос в точности так, как он задан, и попыткой предоставить полезную информацию. Прямой ответ на вопросы, которые он задавал, был бы: «Вероятно, нет. Кто знает?», Но он был бы слишком бесполезен, чтобы стоить беспокойства. В то же время слишком далеко зайти (например, рекомендовать Haskell вместо любого из вышеперечисленного) также вряд ли будет полезно.
Джерри Гроб
3
@ChristianRau: Как вы считаете, что «... большую часть времени std :: for_each - это чистый убыток» не решает вопрос о том, дает ли std :: for_each преимущество?
Джерри Коффин
8

Преимущество написания функционала для того, чтобы быть более читабельным, может не проявляться, когда for(...)и for_each(...).

Если вы используете все алгоритмы в functions.h, вместо использования циклов for, код становится намного более читабельным;

iterator longest_tree = std::max_element(forest.begin(), forest.end(), ...);
iterator first_leaf_tree = std::find_if(forest.begin(), forest.end(), ...);
std::transform(forest.begin(), forest.end(), firewood.begin(), ...);
std::for_each(forest.begin(), forest.end(), make_plywood);

является гораздо более удобным для чтения , чем;

Forest::iterator longest_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
   if (*it > *longest_tree) {
     longest_tree = it;
   }
}

Forest::iterator leaf_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
   if (it->type() == LEAF_TREE) {
     leaf_tree  = it;
     break;
   }
}

for (Forest::const_iterator it = forest.begin(), jt = firewood.begin(); 
     it != forest.end(); 
     it++, jt++) {
          *jt = boost::transformtowood(*it);
    }

for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
    std::makeplywood(*it);
}

И это то, что я считаю очень хорошим, обобщить циклы for в одну строку функций =)

Виктор Сер
источник
6

Легко: for_eachполезно, когда у вас уже есть функция для обработки каждого элемента массива, поэтому вам не нужно писать лямбду. Конечно, это

for_each(a.begin(), a.end(), a_item_handler);

лучше, чем

for(auto& item: a) {
    a_item_handler(a);
}

Кроме того, ранжированный forцикл перебирает только целые контейнеры от начала до конца, в то время for_eachкак более гибок.

Тигран Салуев
источник
4

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 (сотрудник вовлек меня в это)

Дэвид Родригес - дрибеи
источник
3

Помимо читабельности и производительности, одним из аспектов, который обычно упускается из виду, является последовательность. Существует много способов реализовать цикл for (или while) для итераторов:

for (C::iterator iter = c.begin(); iter != c.end(); iter++) {
    do_something(*iter);
}

чтобы:

C::iterator iter = c.begin();
C::iterator end = c.end();
while (iter != end) {
    do_something(*iter);
    ++iter;
}

со многими примерами между на разных уровнях эффективности и потенциальных ошибок.

Однако использование for_each обеспечивает согласованность, абстрагируя цикл:

for_each(c.begin(), c.end(), do_something);

Единственное, о чем вам нужно сейчас беспокоиться: реализуете ли вы тело цикла как функцию, функтор или лямбду, используя функции Boost или C ++ 0x? Лично я предпочел бы беспокоиться об этом, чем о том, как реализовать или прочитать случайный цикл for / while.

Джейсон Говиг
источник
3

Я привык не любить std::for_each и думал, что без лямбды это было сделано совершенно неправильно. Однако некоторое время назад я передумал, и теперь мне это действительно нравится. И я думаю, что это даже улучшает читабельность и облегчает тестирование вашего кода способом TDD.

std::for_eachАлгоритм может быть прочитан как сделать кое - что со всеми элементами в диапазоне , которые могут улучшить читаемость. Скажем, действие, которое вы хотите выполнить, имеет длину 20 строк, а функция, где выполняется действие, также имеет длину около 20 строк. Это сделало бы функцию длиной в 40 строк с обычным циклом for и только около 20 с std::for_each, таким образом, вероятно, легче понять.

Функторы для std::for_eachболее вероятно будут более универсальными и, следовательно, могут использоваться повторно, например:

struct DeleteElement
{
    template <typename T>
    void operator()(const T *ptr)
    {
        delete ptr;
    }
};

И в коде у вас будет только однострочная строка, std::for_each(v.begin(), v.end(), DeleteElement())которая немного лучше IMO, чем явный цикл.

Все эти функторы обычно легче пройти в модульных тестах, чем в явном цикле for в середине длинной функции, и это само по себе уже большая победа для меня.

std::for_each также, как правило, более надежный, так как вы с меньшей вероятностью ошибетесь с дальностью.

И, наконец, компилятор может генерировать немного лучший код, std::for_eachчем для определенных типов созданного вручную цикла for, так как он (for_each) всегда выглядит одинаково для компилятора, и разработчики компилятора могут использовать все свои знания, чтобы сделать его настолько хорошим, насколько они жестяная банка.

То же относится и к другим стандартным алгоритмам, таким как find_ifи transformт. Д.

Дмитрий
источник
2

forпредназначен для цикла, который может повторять каждый элемент или каждый третий и т. д. for_eachпредназначен для повторения только каждого элемента. Это ясно из его названия. Так что более понятно, что вы собираетесь делать в своем коде.

Кирилл В. Лядвинский
источник
4
нет, если вы передадите ему итератор, который увеличивается на 3 с каждым ++. Может быть, необычно, но цикл for делает то же самое.
Potatoswatter
В этом случае, возможно, лучше использовать, transformчтобы не путать кого-либо.
Кирилл В. Лядвинский
2

Если вы часто используете другие алгоритмы из STL, есть несколько преимуществ for_each:

  1. Это часто будет проще и менее подвержено ошибкам, чем цикл for, отчасти потому, что вы будете использовать функции с этим интерфейсом, а отчасти потому, что во многих случаях это на самом деле немного более кратко.
  2. Хотя цикл for на основе диапазона может быть еще проще, он менее гибок (как отметил Адриан МакКарти, он выполняет итерации по всему контейнеру).
  3. В отличие от традиционного цикла for, for_eachвы вынуждены писать код, который будет работать для любого входного итератора. Ограничение таким образом может быть хорошей вещью, потому что:

    1. Возможно, вам потребуется адаптировать код для работы с другим контейнером позже.
    2. В начале это может научить вас чему-то и / или изменить ваши привычки к лучшему.
    3. Даже если вы всегда будете писать для циклов, которые абсолютно эквивалентны, другие люди, которые изменяют тот же код, могут не делать этого без запроса на использование for_each.
  4. Использование for_eachиногда делает более очевидным, что вы можете использовать более специфическую функцию STL, чтобы сделать то же самое. (Как и в примере с Джерри Коффином; это не обязательно for_eachлучший вариант, но цикл for - не единственная альтернатива.)

Шон Патрик Сантос
источник
2

С C ++ 11 и двумя простыми шаблонами вы можете написать

        for ( auto x: range(v1+4,v1+6) ) {
                x*=2;
                cout<< x <<' ';
        }

в качестве замены for_eachили петли. Почему выбрать это сводится к краткости и безопасности, нет шансов на ошибку в выражении, которого нет.

Для меня, for_each всегда было лучше по тем же причинам, когда тело цикла уже является функтором, и я воспользуюсь любым преимуществом, которое смогу получить.

Вы все еще используете три выражения for, но теперь, когда вы видите одно, вы знаете, что там есть что-то, что нужно понять, это не шаблон. Я ненавижу шаблон. Я негодую на его существование. Это не настоящий код, читать его нечему, это просто еще одна вещь, требующая проверки. Умственное усилие может быть измерено тем, насколько легко заржаветь при проверке.

Шаблоны

template<typename iter>
struct range_ { 
                iter begin() {return __beg;}    iter end(){return __end;}
            range_(iter const&beg,iter const&end) : __beg(beg),__end(end) {}
            iter __beg, __end;
};

template<typename iter>
range_<iter> range(iter const &begin, iter const &end)
    { return range_<iter>(begin,end); }
jthill
источник
1

В основном вам придется перебирать всю коллекцию . Поэтому я предлагаю вам написать свой собственный вариант for_each (), принимая только 2 параметра. Это позволит вам переписать пример Терри Махаффи следующим образом:

for_each(container, [](int& i) {
    i += 10;
});

Я думаю, что это действительно более читабельно, чем цикл. Однако для этого требуются расширения компилятора C ++ 0x.

Димитрий С.
источник
1

Я считаю, что for_each плохо для читабельности. Идея хорошая, но с ++ очень трудно писать для чтения, по крайней мере, для меня. c ++ 0x лямда-выражения помогут. Мне очень нравится идея лямда. Однако, на первый взгляд, я думаю, что синтаксис очень уродливый, и я не уверен на 100%, что когда-нибудь к нему привыкну. Может быть, через 5 лет я к этому привыкну и не подумаю, а может и нет. Время покажет :)

Я предпочитаю использовать

vector<thing>::iterator istart = container.begin();
vector<thing>::iterator iend = container.end();
for(vector<thing>::iterator i = istart; i != iend; ++i) {
  // Do stuff
}

Я нахожу явное для цикла более понятным для чтения, а расширенное использование именованных переменных для начального и конечного итераторов уменьшает беспорядок в цикле for.

Конечно, случаи меняются, это как раз то, что я обычно нахожу лучше всего.

jcoder
источник
0

Итератор может быть вызовом функции, которая выполняется на каждой итерации цикла.

Смотрите здесь: http://www.cplusplus.com/reference/algorithm/for_each/

sdornan
источник
2
Сообщения, содержащие только ссылки, не дают хороших ответов, и в любом случае, где в этой ссылке показано что-то похожее на вызываемый итератор? Я уверен, что эта концепция просто не имеет смысла. Возможно, вы просто суммировали, что for_eachделает, и в этом случае он не отвечает на вопрос о его преимуществах.
underscore_d
0

Для цикла может сломаться; Я не хочу быть попугаем для Херба Саттера, поэтому вот ссылка на его презентацию: http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T Обязательно прочитайте также комментарии :)

NoSenseEtAl
источник
0

for_eachПозвольте нам реализовать шаблон Fork-Join . Помимо этого он поддерживает Fluent-интерфейс .

шаблон вилки

Мы можем добавить реализацию gpu::for_eachдля использования cuda / gpu для гетерогенно-параллельных вычислений, вызвав лямбда-задачу у нескольких работников.

gpu::for_each(users.begin(),users.end(),update_summary);
// all summary is complete now
// go access the user-summary here.

И gpu::for_each может дождаться окончания работы над всеми лямбда-задачами перед выполнением следующих операторов.

свободно-интерфейс

Это позволяет нам писать понятный человеку код в сжатой форме.

accounts::erase(std::remove_if(accounts.begin(),accounts.end(),used_this_year));
std::for_each(accounts.begin(),accounts.end(),mark_dormant);
Шува
источник