Ответ 1
Я думаю, что есть некоторые другие различия, пока не затронутые ответами.
-
a
for_each
может принимать любой подходящий вызываемый объект, позволяя "перерабатывать" тело цикла для разных циклов. Например (псевдокод)for( range_1 ) { lengthy_loop_body } // many lines of code for( range_2 ) { lengthy_loop_body } // the same many lines of code again
становится
auto loop_body = some_lambda; // many lines of code here only std::for_each( range_1 , loop_body ); // a single line of code std::for_each( range_2 , loop_body ); // another single line of code
тем самым избегая дублирования и упрощая обслуживание кода. (Разумеется, в смешном сочетании стилей можно использовать аналогичный подход с циклом
for
.) -
другое отличие касается выхода из цикла (с
break
илиreturn
в циклеfor
). Насколько мне известно, в циклеfor_each
это может быть сделано только путем исключения исключения. Напримерfor( range ) { some code; if(condition_1) return x; // or break more code; if(condition_2) continue; yet more code; }
становится
try { std::for_each( range , [] (const_reference x) { some code; if(condition_1) throw x; more code; if(condition_2) return; yet more code; } ); } catch(const_reference r) { return r; }
с теми же эффектами относительно вызова деструкторов для объектов с областью действия тела цикла и тела функции (вокруг цикла).
-
Основное преимущество
for_each
- IMHO, что можно перегрузить его для определенных типов контейнеров, когда простая итерация не так эффективна. Например, рассмотрим контейнер, который содержит связанный список блоков данных, каждый блок, содержащий смежный массив элементов, аналогичный (опускающий нерелевантный код)namespace my { template<typename data_type, unsigned block_size> struct Container { struct block { const block*NEXT; data_type DATA[block_size]; block() : NEXT(0) {} } *HEAD; }; }
то соответствующий имперский итератор для этого типа потребует проверки конца блока при каждом приращении, и оператору сравнения необходимо сравнить как указатель блока, так и индекс в каждом блоке (исключая нерелевантный код):
namespace my { template<typename data_type, unsigned block_size> struct Container { struct iterator { const block*B; unsigned I; iterator() = default; iterator&operator=(iterator const&) = default; iterator(const block*b, unsigned i) : B(b), I(i) {} iterator& operator++() { if(++I==block_size) { B=B->NEXT; I=0; } // one comparison and branch return*this; } bool operator==(const iterator&i) const { return B==i.B && I==i.I; } // one or two comparisons bool operator!=(const iterator&i) const { return B!=i.B || I!=i.I; } // one or two comparisons const data_type& operator*() const { return B->DATA[I]; } }; iterator begin() const { return iterator(HEAD,0); } iterator end() const { return iterator(0,0); } }; }
этот тип итератора корректно работает с
for
иfor_each
, напримерmy::Container<int,5> C; for(auto i=C.begin(); i!=C.end(); // one or two comparisons here ++i) // one comparison here and a branch f(*i);
но требует от двух до трех сравнений на итерацию, а также ветку. Более эффективным способом является перегрузка функции
for_each()
на цикл указателя блока и индекса отдельно:namespace my { template<typename data_type, int block_size, typename FuncOfDataType> FuncOfDataType&& for_each(typename my::Container<data_type,block_size>::iterator i, typename my::Container<data_type,block_size>::iterator const&e, FuncOfDataType f) { for(; i.B != e.B; i.B++,i.I=0) for(; i.I != block_size; i.I++) f(*i); for(; i.I != e.I; i.I++) f(*i); return std::move(f); } } using my::for_each; // ensures that the appropriate using std::for_each; // version of for_each() is used
который требует только одного сравнения для большинства итераций и не имеет ответвлений (обратите внимание, что ветки могут иметь неприятное влияние на производительность). Обратите внимание, что нам не нужно определять это в пространстве имен
std
(что может быть незаконным), но может гарантировать, что правильная версия используется соответствующими директивамиusing
. Это эквивалентноusing std::swap;
при специализированииswap()
для определенных пользовательских типов.