std :: for_each over for循环的优点

std::for_each for循环有什么优点吗? 对我来说, std::for_each似乎只是阻碍了代码的可读性。 为什么一些编码标准推荐使用?

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#中已经有一段时间了,实际上,在我看到的每个Java或C#代码中,实际上有更多的foreach循环比经典for循环更多。

这是一些原因:

  1. 这似乎妨碍了可读性,因为你不习惯它和/或不使用正确的工具,使它很容易。 (请参阅boost :: range和boost :: bind / boost :: lambda for helpers。其中许多将进入C ++ 0x并使for_each和相关函数更有用。

  2. 它允许您在for_each之上编写一个可以与任何迭代器一起工作的algorithm。

  3. 它减less了愚蠢的键入错误的机会。

  4. 它也打开了你的想法,其余的STLalgorithm,如find_ifsortreplace等,这些不会看起来很奇怪了。 这可能是一个巨大的胜利。

更新1:

最重要的是,它可以帮助你超越for_each和for循环,就像所有这些,看看其他的STL-alogs,比如find / sort / partition / copy_replace_if,并行执行等。

大量的处理可以用for_each的兄弟姐妹的“其余部分”来简洁地写出来,但是如果你只是写一个带有各种内部逻辑的for循环,那么你将永远不会学习如何使用它们,最终发明轮子一遍又一遍。

(即将推出的范围式for_each):

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

或者用C ++ x11 lambdas:

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

国际海事组织更可读:

 for(Monsters::iterator i = monsters.begin(); i != monsters.end(); ++i) { i->think(); } 

另外这个(或与lambdas,见其他人):

 for_each(bananas, boost::bind(&Monkey::eat, my_monkey, _1)); 

比以下简洁:

 for(Bananas::iterator i = banans.begin(); i != bananas.end(); ++i) { my_monkey->eat(*i); } 

特别是如果你有几个函数来调用,但也许这只是我。 ;)

更新2 :我写了我自己的单线程封装的stl-algos的范围,而不是一对迭代器。 boost :: range_ex,一旦发布,将包括,也许它会在那里在C + + 0x也?

for_each更通用。 您可以使用它来遍历任何types的容器(通过传入开始/结束迭代器)。 你可以用一个使用for_each的函数replace容器,而不必更新迭代代码。 你需要考虑世界上还有其他容器比std :: vector和普通的旧C数组来看看for_each的优点。

for_each的主要缺点是它需要一个函数,所以语法很笨重。 这是在C ++ 0x中解决的,引入了lambdas:

 std::vector<int> container; ... std::for_each(container.begin(), container.end(), [](int& i){ i+= 10; }); 

这在3年内对你来说不会奇怪。

就个人而言,任何时候我需要走出我的方式来使用std::for_each (编写特殊用途functors /复杂boost::lambda s),我觉得BOOST_FOREACH和C ++ 0x的基于范围的清晰:

 BOOST_FOREACH(Monster* m, monsters) { if (m->has_plan()) m->act(); } 

VS

 std::for_each(monsters.begin(), monsters.end(), if_then(bind(&Monster::has_plan, _1), bind(&Monster::act, _1))); 

它是非常主观的,有人会说使用for_each会使代码更具可读性,因为它允许用相同的约定来处理不同的集合。 for_each itslef是作为一个循环来实现的

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

所以它取决于你select什么是适合你的。

像许多algorithm函数一样,最初的反应是认为使用foreach比循环更不可读。 这是许多火焰战争的话题。

一旦你习惯了这个成语,你可能会觉得它很有用。 一个明显的优点是它迫使编码器将循环的内容与实际的迭代function分开。 (好吧,我认为这是一个优势,别人说你只是简单地把代码砍掉而已)。

另一个优点是,当我看到foreach时,我知道每个项目都将被处理或抛出exception。

for循环允许几个选项来终止循环。 您可以让循环运行完整的过程,也可以使用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; } } } 

你大多是正确的:大多数时候, std::for_each是一个净亏损。 我甚至会比较for_eachgotogoto提供了最通用的stream量控制 – 您可以使用它来实现任何其他可以想象的控制结构。 然而,这种多function性意味着单独看到一个goto ,几乎告诉你在这种情况下它打算做什么。 因此,除了作为最后的手段之外,几乎没有人能够正确地使用goto

在标准algorithm中, for_each方法大致相同 – 它可以用来实现任何事情,这意味着看到for_each几乎不会告诉你在这种情况下它被用于什么。 不幸的是,人们对于for_each个人的态度是,他们对转向的态度是在1970年左右 – 有几个人已经意识到了它只能作为最后的手段,但是很多人仍然认为它是主要的algorithm,并且很less使用任何其他的。 绝大多数时候,即使快速浏览一下,都会发现其中一个select是非常优越的。

举个例子,我敢肯定,我已经失去了多less次看到人们使用for_each编写代码来打印集合的内容。 根据我看到的post,这可能是for_each最常用的一种。 他们最终的结果是:

 class XXX { // ... public: std::ostream &print(std::ostream &os) { return os << "my data\n"; } }; 

他们的post询问了bind1stmem_fun等他们需要做些什么的组合:

 std::vector<XXX> coll; std::for_each(coll.begin(), coll.end(), XXX::print); 

工作,并打印出coll的元素。 如果确实按照我在那里写的那样工作,那将是平庸的,但事实并非如此 – 当你使用它的时候,很难find那些与什么有关的代码继续把它们连在一起。

幸运的是,有一个更好的方法。 为XXX添加正常的stream插入器重载:

 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

for(...)for_each(... )时,编写函数的可读性更好的优点可能不会显示出来。

如果你在functional.h中使用所有的algorithm,而不是使用for循环,那么代码就会变得更加可读。

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

这就是我认为是非常好的,将for循环概括为一行函数=)

for_each循环意味着隐藏用户代码中的迭代器(如何实现循环的细节),并在操作上定义清晰的语义:每个元素只会迭代一次。

在当前标准中可读性的问题是它需要一个仿函数作为最后一个参数而不是代码块,所以在许多情况下,你必须为它编写特定的仿函数types。 由于函子对象不能在位置定义(函数中定义的本地类不能用作模板参数),并且必须将实现的循环从实际循环移开,所以变成可读性较差的代码。

 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::bindstd::bind在下一个标准中),或者boost::lambda (下一个标准中的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 } 

如果你有function/方法来调用,那么它比手写版本更具可读性和更紧凑。 该实现可以提供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 } 

改善代码的位置:当你浏览你看到它在做什么。 事实上,你甚至不需要使用类语法来定义函数,而是在那里使用lambda:

 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(一名同事把我引入)

除了可读性和性能,通常忽略的一个方面是一致性。 有很多方法可以通过迭代器实现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特性来实现循环体作为函数,仿函数或lambda? 就个人而言,我宁愿担心,而不是如何实现或阅读随机for / while循环。

for是for循环,可以迭代每个元素或每三分之一等for_each是只迭代每个元素。 它的名字很清楚。 所以更清楚你打算在你的代码中做什么。

我曾经不喜欢std::for_each并认为没有lambda,这是完全错误的。 不过前段时间我确实改变了想法,现在我真的很喜欢。 而且我认为它甚至可以提高可读性,并且使得以TDD方式testing代码变得更加容易。

std::for_eachalgorithm可以像读取范围内的所有元素那样读取,这可以提高可读性。 假设你想要执行的动作是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())这样的std::for_each(v.begin(), v.end(), DeleteElement()) ,这比稍微更好的IMO比显式循环更好。

所有这些函数在unit testing中通常比在一个长函数中明确的for循环更容易,而这对我来说已经是一个巨大的胜利。

std::for_each通常也更可靠,因为你不太可能在范围上犯一个错误。

最后,编译器可能会为std::for_each生成稍微好一些的代码,而不是某些types的手工循环,因为它(for_each)对于编译器来说总是看起来是一样的,编译器编写者可以把所有的知识尽可能好。

同样适用于其他stdalgorithm,如find_iftransform

用C ++ 11和两个简单​​的模板,你可以编写

  for ( auto x: range(v1+4,v1+6) ) { x*=2; cout<< x <<' '; } 

作为for_each或循环的替代。 为什么select它可以归结为简洁和安全,没有一个expression式不存在错误的机会。

对于我来说,当循环体已经是一个仿函数的时候, for_each总是以相同的理由更好,我会利用任何我能得到的优势。

你仍然使用三个expression式,但是现在当你看到一个你知道的东西时,有一些东西需要理解,这不是样板。 我讨厌样板。 我恨它的存在。 这不是真正的代码,阅读它没有什么可学的,只是需要检查的一件事。 精神上的努力可以通过检查它是多么容易生锈来衡量。

模板是

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

简单:当你已经有一个函数来处理每一个数组项时, for_each是有用的,所以你不必写lambda。 当然,这个

 for_each(a.begin(), a.end(), a_item_handler); 

比…更好

 for(auto& item: a) { a_item_handler(a); } 

此外,范围for循环只遍历整个容器for_each ,而for_each更灵活。

我发现for_each对可读性不好。 这个概念是一个很好的概念,但是c ++使得写可读性非常困难,至less对我而言是这样。 C ++ 0x lamdaexpression式将有所帮助。 我真的很喜欢拉姆达斯的想法。 然而,乍一看,我认为这个语法非常难看,我不能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 } 

我发现一个明确的循环更清晰的阅读和明确使用命名variables的开始和结束迭代器减less杂乱的for循环。

当然,情况各不相同,这正是我通常发现最好的。

如果您经常使用STL中的其他algorithm,则for_each有几个优点:

  1. 它通常比for循环更简单,更容易出错,部分原因是你将习惯于使用这个接口,部分原因是在很多情况下它实际上更简洁一些。
  2. 虽然基于范围的for循环可能更简单,但它不那么灵活(正如Adrian McCarthy所指出的,它遍历整个容器)。
  3. 与传统的for循环不同, for_each强制您编写可用于任何input迭代器的代码。 以这种方式受到限制实际上可能是一件好事,因为:

    1. 您可能实际上需要稍后调整代码以适应不同的容器。
    2. 一开始,它可能会教你一些事情和/或改变你的习惯。
    3. 即使您总是编写完全相同的循环,但修改相同代码的其他人可能不会在不提示使用for_each情况下执行此操作。
  4. 使用for_each有时候更明显的是你可以使用更具体的STL函数来做同样的事情。 (就像Jerry Coffin的例子一样, for_each不一定是最好的select,但for循环不是唯一的select。)

您可以让迭代器对通过循环的每次迭代执行的函数进行调用。

看到这里: http : //www.cplusplus.com/reference/algorithm/for_each/

大多数情况下,你将不得不遍历整个集合 。 因此,我build议你编写自己的for_each()变体,只需要2个参数。 这将允许你重写特里马哈菲的例子 :

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

我认为这比for循环更可读。 但是,这需要C ++ 0x编译器扩展。

For循环可以打破; 我不想成为香草萨特的鹦鹉,所以这里是他的演示文稿的链接: http : //channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T一定要阅读的评论也:)