Переваги std :: for_each over для циклу


168

Чи є якісь переваги std::for_eachнад forциклом? Мені std::for_eachздається , це лише заважає читати код. Чому тоді деякі стандарти кодування рекомендують використовувати його?


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 #, і насправді існує більше foreachциклів, ніж у класичних forциклів у кожному недавньому Java-коді чи коді C #.


12
Насправді, цикл foreach з scoop вже давно доступний, і я все одно хочу перебрати 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. Здається, це перешкоджає читаемості лише тому, що ви до цього не звикли та / або не використовуєте правильних інструментів навколо нього, щоб зробити це справді просто. .

  2. Це дозволяє написати алгоритм поверх for_each, який працює з будь-яким ітератором.

  3. Це знижує ймовірність дурного набору помилок.

  4. Вона також відкриває свій розум до іншої частини STL-алгоритмів, як find_if, sort, replaceі т.д. , і це не буде виглядати так дивно більше. Це може бути величезний виграш.

Оновлення 1:

Найголовніше, що це допомагає вам вийти за межі for_eachvs. for-loops, як це все, і подивитися на інші STL-алоги, як-от find / sort / partition / copy_replace_if, паралельне виконання .. або будь-що інше.

Багато обробки можна скласти дуже стисло, використовуючи "решту" братів і сестер for_each, але якщо все, що ви робите, - це написати цикл for-loop з різною внутрішньою логікою, то ви ніколи не навчитеся ними користуватися, і ви врешті-решт винайдіть колесо знову і знову.

І (скоро доступний для діапазону стиль діапазону for_each):

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

Або з лямбдами C ++ x11:

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

чи читається IMO, ніж:

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
(btw: for_eachу другому прикладі неправильно (має бутиfor_each( bananas.begin(), bananas.end(),...
David Rodríguez - dribeas

Я написав обгортки, які використовують діапазони замість двох ітераторів. Вони будуть доступні пізніше (див. Діапазон_ex), але кожен має їх мати. (Додано оновлення про це.)
Макке

1
Підтримка паралельної обробки немає. 1 причина тут. Ми можемо додати реалізацію, щоб використовувати cuda / gpu для неоднорідних паралельних обчислень.
шува

25

for_eachє більш загальним. Ви можете використовувати його для ітерації над будь-яким типом контейнера (пропустивши ітератори початку / кінця). Ви можете потенційно поміняти контейнери під функцію, яка використовує for_eachбез оновлення коду ітерації. Вам потрібно врахувати, що в світі є інші контейнери, ніж std::vectorзвичайний масив С, щоб побачити переваги 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: це буде конструкція ranged-for, і синтаксис не буде читати "for_each" сам по собі: for ( int v : int_vector ) {(навіть якщо його сьогодні можна імітувати за допомогою BOOST_FOREACH)
David Rodríguez - dribeas

@David: Я маю на увазі загальне додавання функцій на основі діапазону (тому ви можете використовувати діапазони з усіма цими функціями for_each, копіювати, видалити_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_eachitlef реалізований у вигляді циклу

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

тож саме від вас вирішити, що саме вам підходить.


11

Як і багато функцій алгоритму, початкова реакція полягає в тому, що вважати, що непередбачуваніше використовувати foreach, ніж цикл. Це було темою багатьох вогняних воєн.

Як тільки ви звикнете до ідіоми, вам це стане корисним. Однією з очевидних переваг є те, що він змушує кодер відокремлювати внутрішній вміст циклу від фактичної функціональності ітерації. (Гаразд, я думаю, що це перевага. Інші кажуть, що ви просто рубаєте код, не маючи справжньої користі).

Ще одна перевага полягає в тому, що коли я бачу передбачення, я знаю, що або кожен предмет буде оброблений, або виняток буде викинуто.

Для контуру дозволяє кілька варіантів завершення циклу. Ви можете дозволити циклу виконувати його повний курс, або ви можете використовувати ключове слово 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. Ви можете думати про це у випадках, коли не застосовується жоден інший алгоритм std. Чи використовуєте ви тоді for_each чи for loop? Подумайте про це і що б ви не придумали, це повинно було відповісти.
Крістіан Рау

@ChristianRau: Завжди є тонка грань між тим, як відповісти на питання точно так, як задали, і намагатися надати корисну інформацію. Пряма відповідь на саме ті запитання, які він задав, буде: "Мабуть, ні. Хто знає?", Але був би надто марним, щоб вартий турбування. У той же час, якщо надто далеко виходити (наприклад, рекомендувати Haskell замість будь-якого з перерахованих вище), швидше за все, це теж не принесе користі.
Джеррі Труну

3
@ChristianRau: Як ви вважаєте, що "... більшість часу std :: for_each - це чиста втрата" не стосується питання про те, чи надає std :: for_each перевагу?
Джеррі Труну

8

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

Якщо ви використовуєте всі алгоритми у funkcional.h, замість того, щоб використовувати for-loops, код отримує набагато більш читабельний;

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);
}

І це те, що я вважаю таким приємним, узагальнюйте фор-петлі до функцій одного рядка =)


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 (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? Особисто я скоріше переживаю це, ніж те, як реалізувати чи прочитати випадковий цикл для / while.


3

Раніше я не любив std::for_eachі думав, що без лямбда це робиться зовсім неправильно. Однак я передумав деякий час тому, і зараз я насправді люблю це. Я думаю, що це навіть покращує читабельність та полегшує тестування коду TDD-способом.

std::for_eachАлгоритм може бути прочитаний як зробити дещо - що з усіма елементами в діапазоні , які можуть поліпшити читаність. Скажіть, що дія, яку ви хочете виконати, становить 20 ліній, а функція, де виконується дія, також становить приблизно 20 рядків. Це зробило б функцію довжиною 40 рядків із звичайною для циклу, і лише приблизно 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, ніж явний цикл.

Усі ці функтори, як правило, простіше потрапити під тести одиниць, ніж явні для циклу в середині довгої функції, і тільки це вже є великим виграшем для мене.

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

І нарешті, компілятор може створити трохи кращий код, std::for_eachніж для певних типів ручної роботи для циклу, оскільки він (for_each) завжди виглядає однаково для компілятора, і автори-компілятори можуть вкласти всі свої знання, щоб зробити це так само добре, як вони може.

Те саме стосується інших std алгоритмів, таких як find_ifі transformт.д.


2

forпризначений для циклу, який може повторювати кожен елемент або кожен третій і т.д., for_eachпризначений для ітерації лише кожного елемента. З його назви видно. Тож зрозуміліше, що ви маєте намір зробити у своєму коді.


4
не якщо ви передасте йому ітератор, який просувається по 3 з кожним ++. Можливо, незвично, але так само і цикл for-петлі робить те саме.
Potatoswatter

У такому випадку, можливо, це буде краще використовувати, transformщоб не плутати когось.
Кирило В. Лядвінський

2

Якщо ви часто використовуєте інші алгоритми STL, є кілька переваг for_each:

  1. Часто це буде простішим і менш схильним до помилок, ніж циклу, частково тому, що ви будете звикати до функцій із цим інтерфейсом, а частково тому, що він фактично трохи більш стислий у багатьох випадках.
  2. Хоча діапазон на основі циклу може бути ще простішим, він менш гнучкий (як зазначає Адріан Маккарті, він перебирає весь контейнер).
  3. На відміну від традиційного для циклу, 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); }

1

Переважно вам доведеться повторити всю колекцію . Тому я пропоную вам написати свій власний варіант for_each (), взявши лише два параметри. Це дозволить вам переписати приклад Террі Махаффі таким чином:

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

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


1

Я вважаю, що for_each погано сприймає читабельність. Концепція хороша, але c ++ дуже важко пише читабельно, принаймні для мене. 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.

Звичайно випадки різні, це якраз те, що зазвичай вважаю найкращим.


0

Ітератор може бути викликом до функції, яка виконується на кожній ітерації через цикл.

Дивіться тут: http://www.cplusplus.com/reference/algorithm/for_each/


2
Повідомлення, що містять лише посилання, не дають хороших відповідей, і як би там не було, де в цьому посиланні відображається щось, що нагадує ітератор дзвінка? Я майже впевнений, що ця концепція просто не має сенсу. Можливо, ви просто підсумовували, що for_eachробить, і в цьому випадку це не відповідає на питання про його переваги.
підкреслюйте_d


0

for_eachдозвольте нам реалізувати шаблон Fork-Join . Крім того, він підтримує вільний інтерфейс .

вилка-приєднується візерунок

Ми можемо додати реалізацію, 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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.