Существует ли какое - либо преимущество std::for_each
над for
циклом? Мне std::for_each
только кажется, что мешает удобочитаемости кода. Почему тогда некоторые стандарты кодирования рекомендуют его использовать?
c++
stl
foreach
coding-style
missingfaktor
источник
источник
std::for_each
при использовании сboost.lambda
илиboost.bind
часто может улучшить читаемостьОтветы:
С C ++ 11 (ранее называвшимся C ++ 0x) приятно то , что эта утомительная дискуссия будет решена.
Я имею в виду, никто в здравом уме, кто хочет перебрать всю коллекцию, все равно будет использовать это
Или это
когда доступен синтаксис цикла на основе диапазона
for
:Этот вид синтаксиса был доступен в Java и C # в течение некоторого времени, и на самом деле в каждом недавнем коде Java или C #, который я видел, имеется гораздо больше
foreach
циклов, чем классическихfor
циклов.источник
Element & e
еслиauto & e
(илиauto const &e
) выглядит лучше. Я бы использовалElement const e
(без ссылки), когда я хочу неявное преобразование, скажем, когда источник представляет собой коллекцию разных типов, и я хочу, чтобы они преобразовывались вElement
.Вот несколько причин:
Кажется, это затрудняет читабельность только потому, что вы к этому не привыкли и / или не используете правильные инструменты, чтобы сделать его действительно простым. (см. boost :: range и boost :: bind / boost :: lambda для помощников. Многие из них войдут в C ++ 0x и сделают for_each и связанные функции более полезными.)
Это позволяет вам написать алгоритм поверх for_each, который работает с любым итератором.
Это уменьшает вероятность глупых ошибок ввода.
Она также открывает свой ум к остальной части STL-алгоритмов, как
find_if
,sort
,replace
и т.д. , и это не будет выглядеть так странно больше. Это может быть огромной победой.Обновление 1:
Самое главное, это поможет вам выйти за рамки
for_each
цикла for, как будто это все, что есть, и посмотреть на другие STL-файлы, такие как find / sort / partition / copy_replace_if, параллельное выполнение ... или что-то еще.Большая обработка может быть написана очень кратко, используя «остальную часть» братьев и сестер for_each, но если все, что вы делаете, это пишете цикл for с различной внутренней логикой, то вы никогда не научитесь использовать их, и вы в конечном итоге изобретать колесо снова и снова.
И (скоро будет доступен диапазон в стиле for_each):
Или с C ++ x11 лямбдами:
ИМО более читабельно, чем:
Также это (или с лямбдами, см. Другие):
Является более кратким, чем:
Особенно, если у вас есть несколько функций для вызова по порядку ... но, возможно, это только я. ;)
Обновление 2 : я написал свои собственные однострочные оболочки stl-algos, которые работают с диапазонами вместо пары итераторов. Однажды выпущенный boost :: range_ex будет включать его, и, возможно, он будет и в C ++ 0x?
источник
outer_class::inner_class::iterator
или они являются шаблонными аргументами:typename std::vector<T>::iterator
... сама конструкция for может натолкнуться наfor_each
во втором примере неверно (должно бытьfor_each( bananas.begin(), bananas.end(),...
for_each
является более общим. Вы можете использовать его для перебора любого типа контейнера (передавая итераторы начала / конца). Вы можете поменять контейнеры под функцией, котораяfor_each
не требует обновления кода итерации. Вам нужно учитывать, что в мире есть и другие контейнеры, кромеstd::vector
обычных C-массивов, чтобы увидеть преимуществаfor_each
.Основным недостатком
for_each
является то, что требуется функтор, поэтому синтаксис неуклюжий. Это исправлено в C ++ 11 (ранее C ++ 0x) с введением лямбд:Это не будет выглядеть странно для вас через 3 года.
источник
for ( int v : int_vector ) {
(даже если сегодня его можно смоделировать с помощью BOOST_FOREACH)std::for_each(container, [](int& i){ ... });
. Я имею в виду, почему один вынужден писать контейнер дважды?container.each { ... }
без упоминания начальных и конечных итераторов. Я считаю излишним, что мне приходится все время указывать конечный итератор.Лично, в любое время, когда мне нужно было бы выйти из-под контроля
std::for_each
(написать функторы специального назначения / сложныеboost::lambda
), я нахожуBOOST_FOREACH
и диапазон C ++ 0x для более понятного:против
источник
Это очень субъективно, некоторые скажут, что использование
for_each
сделает код более читабельным, так как позволяет обрабатывать разные коллекции с одинаковыми соглашениями.for_each
его реализация реализована в виде циклатак что вам решать, что подходит именно вам.
источник
Как и многие функции алгоритма, первоначальная реакция заключается в том, что думать, что использовать foreach более нечитаемо, чем цикл. Это была тема многих огненных войн.
Как только вы привыкнете к идиоме, вы можете найти ее полезной. Одно очевидное преимущество заключается в том, что он заставляет кодировщика отделить внутреннее содержимое цикла от фактической функциональности итерации. (Хорошо, я думаю, что это преимущество. Другие говорят, что вы просто нарезаете код без реальной выгоды).
Еще одно преимущество заключается в том, что когда я вижу foreach, я знаю, что либо каждый элемент будет обработан, либо будет сгенерировано исключение.
Цикл for позволяет несколько вариантов завершения цикла. Вы можете позволить циклу пройти полный курс, или вы можете использовать ключевое слово break, чтобы явно выпрыгнуть из цикла, или использовать ключевое слово return, чтобы выйти из всего промежуточного цикла функции. В отличие от foreach не позволяет использовать эти параметры, что делает его более читабельным. Вы можете просто взглянуть на имя функции и узнать всю природу итерации.
Вот пример запутанной для цикла:
источник
std::for_each()
в старом стандарте (который использовался во время публикации) вы должны использовать именованный функтор, который, как вы говорите, обеспечивает удобочитаемость и запрещает преждевременное прерывание цикла. Но тогда эквивалентныйfor
цикл не имеет ничего, кроме вызова функции, и это также запрещает преждевременное прерывание. Но кроме этого , я думаю , что вы сделали отличную точку, говоря , чтоstd::for_each()
навязывает , проходящие через весь диапазон.Вы в основном правы: большую часть времени
std::for_each
это чистый убыток. Я бы пошел так далеко, чтобы сравнитьfor_each
сgoto
.goto
обеспечивает максимально универсальное управление потоком данных - вы можете использовать его для реализации практически любой другой структуры управления, которую только можете себе представить. Однако именно эта универсальность означает, что видениеgoto
изолированно практически ничего не говорит о том, что он должен делать в этой ситуации. В результате почти никто в здравом уме не используетgoto
кроме как в крайнем случае.Среди стандартных алгоритмов
for_each
практически такой же способ - его можно использовать для реализации практически всего, что означает, что видениеfor_each
практически ничего не говорит о том, для чего он используется в этой ситуации. К сожалению, отношение людей к тому,for_each
где их отношениеgoto
было, скажем, в 1970 году или около того - несколько человек поняли, что его следует использовать только в качестве крайней меры, но многие все еще считают его основным алгоритмом, и редко, если когда-либо использовать любой другой. Подавляющее большинство времени, даже быстрый взгляд показал бы, что одна из альтернатив была значительно выше.Например, я почти уверен, что потерял счет, сколько раз я видел людей, пишущих код для распечатки содержимого коллекции с использованием
for_each
. Судя по сообщениям, которые я видел, это может быть наиболее распространенное использованиеfor_each
. В итоге они получают что-то вроде:И их пост спрашивает о том, какой комбинации
bind1st
,mem_fun
и т.д. они должны сделать что - то вроде:работать, и распечатать элементы
coll
. Если бы это действительно работало именно так, как я написал там, это было бы посредственно, но это не так - и к тому времени, когда вы заставили это работать, трудно найти те несколько кусочков кода, связанных с тем, что происходит среди частей, которые держат его вместе.К счастью, есть гораздо лучший способ. Добавьте обычную перегрузку потоковой вставки для XXX:
и использовать
std::copy
:Это работает - и практически не требует никакой работы, чтобы выяснить, что печатает содержимое
coll
вstd::cout
.источник
boost::mem_fn(&XXX::print)
а неXXX::print
std::cout
качестве аргумента, чтобы он работал).Преимущество написания функционала для того, чтобы быть более читабельным, может не проявляться, когда
for(...)
иfor_each(...
).Если вы используете все алгоритмы в functions.h, вместо использования циклов for, код становится намного более читабельным;
является гораздо более удобным для чтения , чем;
И это то, что я считаю очень хорошим, обобщить циклы for в одну строку функций =)
источник
Легко:
for_each
полезно, когда у вас уже есть функция для обработки каждого элемента массива, поэтому вам не нужно писать лямбду. Конечно, этолучше, чем
Кроме того, ранжированный
for
цикл перебирает только целые контейнеры от начала до конца, в то времяfor_each
как более гибок.источник
for_each
Петля предназначена , чтобы скрыть итераторы (подробно о том , как реализуется цикл) из кода пользователя и четко определить семантику по работе: каждый элемент будет повторяться ровно один раз.Проблема с удобочитаемостью в текущем стандарте состоит в том, что он требует функтора в качестве последнего аргумента вместо блока кода, поэтому во многих случаях вы должны написать для него определенный тип функтора. Это превращается в менее читаемый код, так как объекты функтора не могут быть определены на месте (локальные классы, определенные внутри функции, не могут использоваться в качестве аргументов шаблона), и реализация цикла должна быть удалена от фактического цикла.
Обратите внимание, что если вы хотите выполнить определенную операцию с каждым объектом, вы можете использовать
std::mem_fn
, илиboost::bind
(std::bind
в следующем стандарте), илиboost::lambda
(лямбды в следующем стандарте), чтобы упростить его:Который не менее читабелен и более компактен, чем версия, свернутая вручную, если у вас есть функция / метод для вызова на месте. Реализация может обеспечить другие реализации
for_each
цикла (например, параллельная обработка).Предстоящий стандарт по-разному устраняет некоторые недостатки, он учитывает локально определенные классы в качестве аргументов для шаблонов:
Улучшение локализации кода: когда вы просматриваете, вы видите, что он делает прямо там. На самом деле вам даже не нужно использовать синтаксис класса для определения функтора, но используйте лямбду прямо здесь:
Даже если для случая
for_each
будет определенная конструкция, которая сделает ее более естественной:Я склонен смешивать
for_each
конструкцию с петлями, скрученными вручную. Когда мне нужен только вызов существующей функции или метода (for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )
), я обращаюсь кfor_each
конструкции, которая отнимает у кода много материала итератора. Когда мне нужно что-то более сложное, и я не могу реализовать функтор на пару строк выше фактического использования, я запускаю свой цикл (сохраняет операцию на месте). В некритических разделах кода я мог бы пойти с BOOST_FOREACH (сотрудник вовлек меня в это)источник
Помимо читабельности и производительности, одним из аспектов, который обычно упускается из виду, является последовательность. Существует много способов реализовать цикл for (или while) для итераторов:
чтобы:
со многими примерами между на разных уровнях эффективности и потенциальных ошибок.
Однако использование for_each обеспечивает согласованность, абстрагируя цикл:
Единственное, о чем вам нужно сейчас беспокоиться: реализуете ли вы тело цикла как функцию, функтор или лямбду, используя функции Boost или C ++ 0x? Лично я предпочел бы беспокоиться об этом, чем о том, как реализовать или прочитать случайный цикл for / while.
источник
Я привык не любить
std::for_each
и думал, что без лямбды это было сделано совершенно неправильно. Однако некоторое время назад я передумал, и теперь мне это действительно нравится. И я думаю, что это даже улучшает читабельность и облегчает тестирование вашего кода способом TDD.std::for_each
Алгоритм может быть прочитан как сделать кое - что со всеми элементами в диапазоне , которые могут улучшить читаемость. Скажем, действие, которое вы хотите выполнить, имеет длину 20 строк, а функция, где выполняется действие, также имеет длину около 20 строк. Это сделало бы функцию длиной в 40 строк с обычным циклом for и только около 20 сstd::for_each
, таким образом, вероятно, легче понять.Функторы для
std::for_each
более вероятно будут более универсальными и, следовательно, могут использоваться повторно, например:И в коде у вас будет только однострочная строка,
std::for_each(v.begin(), v.end(), DeleteElement())
которая немного лучше IMO, чем явный цикл.Все эти функторы обычно легче пройти в модульных тестах, чем в явном цикле for в середине длинной функции, и это само по себе уже большая победа для меня.
std::for_each
также, как правило, более надежный, так как вы с меньшей вероятностью ошибетесь с дальностью.И, наконец, компилятор может генерировать немного лучший код,
std::for_each
чем для определенных типов созданного вручную цикла for, так как он (for_each) всегда выглядит одинаково для компилятора, и разработчики компилятора могут использовать все свои знания, чтобы сделать его настолько хорошим, насколько они жестяная банка.То же относится и к другим стандартным алгоритмам, таким как
find_if
иtransform
т. Д.источник
for
предназначен для цикла, который может повторять каждый элемент или каждый третий и т. д.for_each
предназначен для повторения только каждого элемента. Это ясно из его названия. Так что более понятно, что вы собираетесь делать в своем коде.источник
++
. Может быть, необычно, но цикл for делает то же самое.transform
чтобы не путать кого-либо.Если вы часто используете другие алгоритмы из STL, есть несколько преимуществ
for_each
:В отличие от традиционного цикла for,
for_each
вы вынуждены писать код, который будет работать для любого входного итератора. Ограничение таким образом может быть хорошей вещью, потому что:for_each
.Использование
for_each
иногда делает более очевидным, что вы можете использовать более специфическую функцию STL, чтобы сделать то же самое. (Как и в примере с Джерри Коффином; это не обязательноfor_each
лучший вариант, но цикл for - не единственная альтернатива.)источник
С C ++ 11 и двумя простыми шаблонами вы можете написать
в качестве замены
for_each
или петли. Почему выбрать это сводится к краткости и безопасности, нет шансов на ошибку в выражении, которого нет.Для меня,
for_each
всегда было лучше по тем же причинам, когда тело цикла уже является функтором, и я воспользуюсь любым преимуществом, которое смогу получить.Вы все еще используете три выражения
for
, но теперь, когда вы видите одно, вы знаете, что там есть что-то, что нужно понять, это не шаблон. Я ненавижу шаблон. Я негодую на его существование. Это не настоящий код, читать его нечему, это просто еще одна вещь, требующая проверки. Умственное усилие может быть измерено тем, насколько легко заржаветь при проверке.Шаблоны
источник
В основном вам придется перебирать всю коллекцию . Поэтому я предлагаю вам написать свой собственный вариант for_each (), принимая только 2 параметра. Это позволит вам переписать пример Терри Махаффи следующим образом:
Я думаю, что это действительно более читабельно, чем цикл. Однако для этого требуются расширения компилятора C ++ 0x.
источник
Я считаю, что for_each плохо для читабельности. Идея хорошая, но с ++ очень трудно писать для чтения, по крайней мере, для меня. c ++ 0x лямда-выражения помогут. Мне очень нравится идея лямда. Однако, на первый взгляд, я думаю, что синтаксис очень уродливый, и я не уверен на 100%, что когда-нибудь к нему привыкну. Может быть, через 5 лет я к этому привыкну и не подумаю, а может и нет. Время покажет :)
Я предпочитаю использовать
Я нахожу явное для цикла более понятным для чтения, а расширенное использование именованных переменных для начального и конечного итераторов уменьшает беспорядок в цикле for.
Конечно, случаи меняются, это как раз то, что я обычно нахожу лучше всего.
источник
Итератор может быть вызовом функции, которая выполняется на каждой итерации цикла.
Смотрите здесь: http://www.cplusplus.com/reference/algorithm/for_each/
источник
for_each
делает, и в этом случае он не отвечает на вопрос о его преимуществах.Для цикла может сломаться; Я не хочу быть попугаем для Херба Саттера, поэтому вот ссылка на его презентацию: http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T Обязательно прочитайте также комментарии :)
источник
for_each
Позвольте нам реализовать шаблон Fork-Join . Помимо этого он поддерживает Fluent-интерфейс .шаблон вилки
Мы можем добавить реализацию
gpu::for_each
для использования cuda / gpu для гетерогенно-параллельных вычислений, вызвав лямбда-задачу у нескольких работников.И
gpu::for_each
может дождаться окончания работы над всеми лямбда-задачами перед выполнением следующих операторов.свободно-интерфейс
Это позволяет нам писать понятный человеку код в сжатой форме.
источник