a for_each 可以接受任何适当的可调用对象,允许为不同的 for 循环“回收”循环体。例如(伪代码)
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 循环。)
另一个区别在于跳出循环(for 循环中的break 或return)。据我所知,在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 的主要好处是,恕我直言,当普通迭代效率不高时,可以为某些容器类型重载它。例如,考虑一个包含数据块链表的容器,每个块包含一个连续的元素数组,类似于(省略不相关的代码)
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 指令使用正确的版本。当特化 swap() 用于某些用户定义的类型时,这等效于 using std::swap;。