std :: for_each优于for循环的优点

mis*_*tor 158 c++ foreach stl coding-style

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

Tho*_*tit 170

有好的事情C++ 11(以前称为C++ 0x中),是这个无聊的争论将尘埃落定.

我的意思是,没有一个心智正常的人,想要迭代整个集合,仍然会使用它

for(auto it = collection.begin(); it != collection.end() ; ++it)
{
   foo(*it);
}
Run Code Online (Sandbox Code Playgroud)

或这个

for_each(collection.begin(), collection.end(), [](Element& e)
{
   foo(e);
});
Run Code Online (Sandbox Code Playgroud)

基于范围的for循环语法可用时:

for(Element& e : collection)
{
   foo(e);
}
Run Code Online (Sandbox Code Playgroud)

这种语法现在已经在Java和C#中使用了一段时间,实际上在我看到的每个最近的Java或C#代码中都有foreach比经典for循环更多的循环.

  • 关于想要整个容器范围的假设不是问题的一部分,所以这只是部分答案. (18认同)
  • 实际上,带有scoop的foreach循环在boost中已经可以使用了很长时间,我仍然希望使用for_each和lambda函数进行迭代. (11认同)
  • Range-for很好,除非你真的需要迭代器(然后就没办法了). (9认同)
  • 请注意,循环遍历元素可能不是您想要做的唯一事情,因此最好使用for_each以便您了解find/partition/copy_replace_if以及其他内容,这实际上是循环的重要内容做. (5认同)
  • 我甚至不使用`Element&e`作为`auto&e`(或`auto const&e`)看起来更好.当我想要隐式转换时,我会使用`Element const e`(没有引用),比如当源是不同类型的集合时,我希望它们转换为`Element`. (4认同)
  • `Element & e` 很可能必须是 `const Element & e` (2认同)
  • @Damon:定义一个范围/容器适配器,其begin()/ end()返回一个迭代器,其value_type是容器的基础迭代器,因此,在取消引用时,您将获得迭代器本身,并且将是自动进行推断的类型:循环变量。这肯定是可行的,因为我在上一份工作中偶尔使用过它。 (2认同)
  • 这不能回答问题。“与 for 循环相比,std::for_each 有什么优势吗?” (2认同)
  • 如果您只想迭代容器的子集怎么办?您不能仅使用 `std::` 功能直接使用 range-for (除非它是 `for (auto& e : boost::make_iterator_range(begin, middle) { ... }`),而您可以使用`std::for_each(开始,中间,...)`。 (2认同)

Mac*_*cke 50

以下是一些原因:

  1. 它似乎只是因为你不习惯它和/或没有使用正确的工具来使它变得非常简单而妨碍了可读性.(请参阅boost :: range和boost :: bind/boost :: lambda for helpers.其中许多将进入C++ 0x并使for_each和相关函数更有用.)

  2. 它允许您在for_each之上编写一个算法,该算法适用于任何迭代器.

  3. 它减少了愚蠢的打字错误的机会.

  4. 它也打开你的心灵的STL的算法休息,像find_if,sort,replace等等,这些将不会显得那么陌生了.这可能是一个巨大的胜利.

更新1:

最重要的是,它可以帮助你超越for_each对抗for循环,就像那样,并查看其他STL-alogs,比如find/sort/partition/copy_replace_if,parallell execution ..或者其他什么.

使用for_each的兄弟姐妹的"其余"可以非常简洁地编写很多处理,但是如果你所做的只是编写一个带有各种内部逻辑的for循环,那么你将永远不会学习如何使用它们,你将会最后一遍又一遍地发明轮子.

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

for_each(monsters, boost::mem_fn(&Monster::think));
Run Code Online (Sandbox Code Playgroud)

或者使用C++ x11 lambda:

for_each(monsters, [](Monster& m) { m.think(); });
Run Code Online (Sandbox Code Playgroud)

IMO比以下内容更具可读性:

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

这也是(或与lambdas,见其他人):

for_each(bananas, boost::bind(&Monkey::eat, my_monkey, _1));
Run Code Online (Sandbox Code Playgroud)

比以下更简洁:

for(Bananas::iterator i = bananas.begin(); i != bananas.end(); ++i) {
    my_monkey->eat(*i);
} 
Run Code Online (Sandbox Code Playgroud)

特别是如果你有几个功能可以按顺序调用......但也许这只是我.;)

更新2:我编写了自己的stl-algos单行封装器,它使用范围而不是迭代器对.boost :: range_ex一旦发布,将包含它,也许它也会在C++ 0x中出现?

  • (顺便说一下:第二个例子中的`for_each`是不正确的(应该是`for_each(bananas.begin(),bananas.end(),...` (4认同)

Ter*_*fey 23

for_each更通用.您可以使用它来迭代任何类型的容器(通过传入开始/结束迭代器).您可以在使用for_each的函数下面交换容器,而无需更新迭代代码.你需要考虑世界上除了std :: vector和普通的旧C数组之外还有其他容器来看看for_each的优点.

for_each的主要缺点是它需要一个仿函数,所以语法很笨拙.通过引入lambdas在C++ 0x中修复了这个问题:

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

这对你来说在3年内看起来并不奇怪.

  • +1.广告当for_each采用容器/范围而不是两个迭代器时,它会更加棒极了. (3认同)
  • @Marcus:这将是ranged-for构造,语法本身不会读取'for_each':`for(int v:int_vector){`(即使今天可以用BOOST_FOREACH模拟它) (2认同)

Unc*_*ens 17

就个人而言,任何时候我都需要不再使用std::for_each(编写专用函子/复杂的boost::lambdas),我发现BOOST_FOREACH和C++ 0x的基于范围的更清晰:

BOOST_FOREACH(Monster* m, monsters) {
     if (m->has_plan()) 
         m->act();
}
Run Code Online (Sandbox Code Playgroud)

VS

std::for_each(monsters.begin(), monsters.end(), 
  if_then(bind(&Monster::has_plan, _1), 
    bind(&Monster::act, _1)));
Run Code Online (Sandbox Code Playgroud)


Alo*_*lon 11

它非常主观,有些人会说使用for_each 会使代码更具可读性,因为它允许使用相同的约定处理不同的集合. for_eachitslef实现为循环

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

因此,您可以选择适合自己的产品.


And*_*erd 10

像许多算法函数一样,最初的反应是认为使用foreach比使用循环更难以理解.这是许多火焰战争的话题.

一旦你习惯了成语,你会发现它很有用.一个明显的优点是它迫使编码器将循环的内部内容与实际的迭代功能分开.(好吧,我认为这是一个优势.其他人说你只是在没有真正的利益的情况下砍掉代码).

另一个优点是,当我看到foreach时,我知道要么处理每个项目,要么抛出异常.

一个用于循环允许用于终止循环的几个选项.您可以让循环运行完整的过程,或者您可以使用break关键字显式跳出循环,或使用return关键字退出整个函数中循环.相比之下,foreach不允许这些选项,这使它更具可读性.您只需浏览一下函数名称,就可以了解迭代的完整性.

这是一个令人困惑的for循环的例子:

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;
      }
   }
}
Run Code Online (Sandbox Code Playgroud)


Jer*_*fin 10

你大多数时候都是正确的:大部分时间std::for_each都是净损失.我竟然比去for_eachgoto.goto提供最通用的流量控制 - 您可以使用它来实现您可以想象的几乎任何其他控制结构.然而,这种多功能性意味着,在这种情况下goto,孤立地看到它几乎没有告诉你它应该做什么.结果,goto除了作为最后的手段之外,几乎没有人在正确的思想中使用.

在标准算法中,for_each大致相同的方式 - 它可以用来实现几乎任何东西,这意味着for_each在这种情况下,视觉几乎没有告诉你它用于什么.不幸的是,人们的态度for_each是关于他们的态度goto在(比方说)1970年左右 - 有些人已经意识到它应该仅作为最后的手段使用,但许多人仍然认为它是主要的算法,并且很少使用任何其他.绝大多数时候,即使是快速浏览,也会发现其中一种替代方案非常优越.

例如,我很确定我已经忘记了多少次我看到人们编写代码来打印出使用的集合内容for_each.根据我见过的帖子,这可能是最常见的用途for_each.他们最终得到的结果如下:

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

而他们的职位是问什么组合bind1st,mem_fun等他们需要类似:

std::vector<XXX> coll;

std::for_each(coll.begin(), coll.end(), XXX::print);
Run Code Online (Sandbox Code Playgroud)

工作,打印出来的元素coll.如果它确实像我在那里写的那样工作,那将是平庸的,但它不会 - 当你使它工作时,很难找到与那些相关的那些代码在将它们组合在一起的碎片中继续进行.

幸运的是,有一个更好的方法.为XXX添加普通的流插入器重载:

std::ostream &operator<<(std::ostream *os, XXX const &x) { 
   return x.print(os);
}
Run Code Online (Sandbox Code Playgroud)

并使用std::copy:

std::copy(coll.begin(), coll.end(), std::ostream_iterator<XXX>(std::cout, "\n"));
Run Code Online (Sandbox Code Playgroud)

这不工作-并且需要在几乎所有没有工作弄清楚,它打印的内容collstd::cout.

  • @ChristianRau:你怎么认为"......大多数情况下,std :: for_each是净亏损"并没有解决std :: for_each是否提供优势的问题? (3认同)
  • @ChristianRau:完全按照要求回答问题和尝试提供有用的信息之间总是有一条微妙的界限。对他提出的问题的直接回答是“可能不会。谁知道呢?”,但这种回答毫无用处,不值得费心。同时,走得太远(例如,推荐 Haskell 而不是上述任何一个)也不太可能有多大用处。 (2认同)

Vik*_*ehr 8

写作功能更有可读性的优点可能不会出现在何时for(...)for_each(...).

如果你使用functional.h中的所有算法,而不是使用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);
Run Code Online (Sandbox Code Playgroud)

多少超过可读;

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);
}
Run Code Online (Sandbox Code Playgroud)

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


Tig*_*uev 6

简单:for_each当您已经有一个处理每个数组项的函数时非常有用,因此您不必编写lambda.当然,这个

for_each(a.begin(), a.end(), a_item_handler);
Run Code Online (Sandbox Code Playgroud)

比...更好

for(auto& item: a) {
    a_item_handler(a);
}
Run Code Online (Sandbox Code Playgroud)

此外,ranged for循环仅从整个容器开始到结束迭代,同时for_each更灵活.