Преимущества цикла std :: for_each over for - PullRequest
155 голосов
/ 12 января 2010

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

Ответы [ 20 ]

3 голосов
/ 12 января 2010

Помимо читабельности и производительности, одним из часто пропускаемых аспектов является последовательность. Существует много способов реализовать цикл 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 голосов
/ 12 января 2010

Раньше я не любил 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 также, как правило, более надежно, поскольку вы 'менее вероятно ошибиться с помощью range.

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

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

2 голосов
/ 12 января 2010

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

2 голосов
/ 18 августа 2013

С 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); }
2 голосов
/ 18 августа 2013

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

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

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

1 голос
/ 12 января 2010

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

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

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

1 голос
/ 12 января 2010

Я считаю, что for_each плохо для читабельности. Идея хорошая, но с ++ очень трудно писать для чтения, по крайней мере, для меня. С ++ 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 голосов
/ 14 ноября 2011

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

0 голосов
/ 09 августа 2018

for_each позволяет нам реализовать шаблон Fork-Join .Кроме этого он поддерживает fluent-interface .

шаблон 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 может дождаться окончания работы над всеми лямбда-задачами перед выполнением следующих операторов.

fluent-interface

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

accounts::erase(std::remove_if(accounts.begin(),accounts.end(),used_this_year));
std::for_each(accounts.begin(),accounts.end(),mark_dormant);
0 голосов
/ 12 января 2010

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

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

...