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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2047414/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:57:11  来源:igfitidea点击:

Advantages of std::for_each over for loop

c++stlforeachcoding-style

提问by missingfaktor

Are there any advantages of std::for_eachover forloop? To me, std::for_eachonly seems to hinder the readability of code. Why do then some coding standards recommend its use?

std::for_eachfor循环有什么好处吗?对我来说,std::for_each似乎只会阻碍代码的可读性。为什么有些编码标准推荐使用它?

回答by Thomas Petit

The nice thing with C++11(previously called C++0x), is that this tiresome debate will be settled.

这种做法的好处与C ++ 11(以前称为C ++ 0x中),是这个无聊的争论将尘埃落定。

I mean, no one in their right mind, who wants to iterate over a whole collection, will still use this

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

for(auto it = collection.begin(); it != collection.end() ; ++it)
{
   foo(*it);
}

Or this

或这个

for_each(collection.begin(), collection.end(), [](Element& e)
{
   foo(e);
});

when the range-based forloopsyntax is available:

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

for(Element& e : collection)
{
   foo(e);
}

This kind of syntax has been available in Java and C# for some time now, and actually there are way more foreachloops than classical forloops in every recent Java or C# code I saw.

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

回答by Macke

Here are some reasons:

以下是一些原因:

  1. It seems to hinder readability just because you're not used to it and/or not using the right tools around it to make it really easy. (see boost::range and boost::bind/boost::lambda for helpers. Many of these will go into C++0x and make for_each and related functions more useful.)

  2. It allows you to write an algorithm on top of for_each that works with any iterator.

  3. It reduces the chance of stupid typing bugs.

  4. It also opens your mind to the rest of the STL-algorithms, like find_if, sort, replace, etc and these won't look so strange anymore. This can be a huge win.

  1. 它似乎阻碍了可读性,因为您不习惯它和/或没有使用正确的工具来使它变得非常容易。(有关帮助程序,请参阅 boost::range 和 boost::bind/boost::lambda。其中许多将进入 C++0x 并使 for_each 和相关函数更有用。)

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

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

  4. 它也打开你的心灵的STL的算法,休息一下就好了find_ifsortreplace等这些不会看起来那么奇怪了。这可能是一个巨大的胜利。

Update 1:

更新 1:

Most importantly, it helps you go beyond for_eachvs. for-loops like that's all there is, and look at the other STL-alogs, like find / sort / partition / copy_replace_if, parallell execution .. or whatever.

最重要的是,它可以帮助您超越for_each与 for 循环之类的所有内容,并查看其他 STL 类,例如 find / sort / partition / copy_replace_if、parallel execution .. 或其他。

A lot of processing can be written very concisely using "the rest" of for_each's siblings, but if all you do is to write a for-loop with various internal logic, then you'll never learn how to use those, and you'll end up inventing the wheel over and over.

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

And (the soon-to-be available range-style for_each):

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

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

Or with C++x11 lambdas:

或者使用 C++x11 lambdas:

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

is IMO more readable than:

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

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

Also this (or with lambdas, see others):

还有这个(或使用 lambdas,请参阅其他人):

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

Is more concise than:

比以下更简洁:

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

Especially if you have several functions to call in order... but maybe that's just me. ;)

特别是如果你有几个函数要按顺序调用......但也许这只是我。;)

Update 2: I've written my own one-liner wrappers of stl-algos that work with ranges instead of pair of iterators. boost::range_ex, once released, will include that and maybe it will be there in C++0x too?

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

回答by Terry Mahaffey

for_eachis more generic. You can use it to iterate over any type of container (by passing in the begin/end iterators). You can potentially swap out containers underneath a function which uses for_eachwithout having to update the iteration code. You need to consider that there are other containers in the world than std::vectorand plain old C arrays to see the advantages of for_each.

for_each更通用。您可以使用它来迭代任何类型的容器(通过传入开始/结束迭代器)。您可以在for_each无需更新迭代代码的情况下替换函数下的容器。您需要考虑到世界上还有其他容器而不是std::vector普通的旧 C 数组,才能看到for_each.

The major drawback of for_eachis that it takes a functor, so the syntax is clunky. This is fixed in C++11 (formerly C++0x) with the introduction of lambdas:

的主要缺点for_each是它需要一个函子,所以语法很笨拙。这在 C++11(以前称为 C++0x)中通过引入 lambdas 得到了修复:

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

This will not look weird to you in 3 years.

这在 3 年内你不会觉得奇怪。

回答by UncleBens

Personally, any time I'd need to go out of my way to use std::for_each(write special-purpose functors / complicated boost::lambdas), I find BOOST_FOREACHand C++0x's range-based for clearer:

就我个人而言,任何时候我需要std::for_each特意使用(编写特殊用途的函子/复杂的boost::lambdas),我都会发现BOOST_FOREACHC++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)));

回答by Andrew Shepherd

Like many of the algorithm functions, an initial reaction is to think it's more unreadable to use foreach than a loop. It's been a topic of many flame wars.

像许多算法函数一样,最初的反应是认为使用 foreach 比使用循环更难读。这是许多火焰War的主题。

Once you get used to the idiom you may find it useful. One obvious advantage is that it forces the coder to separate the inner contents of the loop from the actual iteration functionality. (OK, I think it's an advantage. Other's say you're just chopping up the code with no real benifit).

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

One other advantage is that when I see foreach, I knowthat either every item will be processed or an exception will be thrown.

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

A forloop allows several options for terminating the loop. You can let the loop run its full course, or you can use the breakkeyword to explicitly jump out of the loop, or use the returnkeyword to exit the entire function mid-loop. In contrast, foreachdoes not allow these options, and this makes it more readable. You can just glance at the function name and you know the full nature of the iteration.

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

Here's an example of a confusing forloop:

下面是一个令人困惑的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;
      }
   }
}

回答by Alon

its very subjective, some will say that using for_eachwill make the code morereadable, as it allows to treat different collections with the same conventions. for_eachitslef is implemented as a loop

它非常主观,有人会说使用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;
  }

so its up to you to choose what is right for you.

所以由你来选择适合你的东西。

回答by Jerry Coffin

You're mostly correct: most of the time, std::for_eachis a net loss. I'd go so far as to compare for_eachto goto. gotoprovides the most versatile flow-control possible -- you can use it to implement virtually any other control structure you can imagine. That very versatility, however, means that seeing a gotoin isolation tells you virtually nothingabout what's it's intended to do in this situation. As a result, almost nobody in their right mind uses gotoexcept as a last resort.

你大部分是正确的:大多数时候,std::for_each是净亏损。我竟然比去for_eachgotogoto提供了最通用的流程控制——您可以使用它来实现几乎任何您可以想象的其他控制结构。然而,这种多功能性意味着goto孤立地查看 a几乎不会告诉您在这种情况下它打算做什么。结果,goto除了作为最后的手段之外,几乎没有人在他们正常的头脑中使用。

Among the standard algorithms, for_eachis much the same way -- it can be used to implement virtually anything, which means that seeing for_eachtells you virtually nothing about what it's being used for in this situation. Unfortunately, people's attitude toward for_eachis about where their attitude toward gotowas in (say) 1970 or so -- a fewpeople had caught onto the fact that it should be used only as a last resort, but many still consider it the primary algorithm, and rarely if ever use any other. The vast majority of the time, even a quick glance would reveal that one of the alternatives was drastically superior.

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

Just for example, I'm pretty sure I've lost track of how many times I've seen people writing code to print out the contents of a collection using for_each. Based on posts I've seen, this may well be the single most common use of for_each. They end up with something like:

举个例子,我很确定我已经忘记了有多少次我看到人们编写代码来使用for_each. 根据我看过的帖子,这很可能是for_each. 他们最终得到类似的东西:

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

And their post is asking about what combination of bind1st, mem_fun, etc. they need to make something like:

而他们的职位是问什么组合bind1stmem_fun等他们需要做出这样的:

std::vector<XXX> coll;

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

work, and print out the elements of coll. If it really did work exactly as I've written it there, it would be mediocre, but it doesn't -- and by the time you've gotten it to work, it's difficult to find those few bits of code related to what's going on among the pieces that hold it together.

工作,并打印出 的元素coll。如果它真的像我在那里写的那样工作,那将是平庸的,但事实并非如此——当你让它工作时,很难找到与什么相关的那几段代码在将它结合在一起的碎片之间进行。

Fortunately, there is a much better way. Add a normal stream inserter overload for XXX:

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

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

and use std::copy:

并使用std::copy

std::copy(coll.begin(), coll.end(), std::ostream_iterator<XXX>(std::cout, "\n"));

That does work -- and takes virtually no work at all to figure out that it prints the contents of collto std::cout.

这确实有效——而且几乎不需要做任何工作就可以确定它打印了collto的内容std::cout

回答by Viktor Sehr

The advantage of writing functional for beeing more readable, might not show up when for(...)and for_each(...).

编写函数以提高可读性的优势可能不会在for(...)和 时出现for_each(...)。

If you utilize all algorithms in functional.h, instead of using for-loops, the code gets a lot more readable;

如果你使用function.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);

is muchmore readable than;

多少超过可读;

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

And that is what I think is so nice, generalize the for-loops to one line functions =)

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

回答by Tigran Saluev

Easy: for_eachis useful when you already have a function to handle every array item, so you don't have to write a lambda. Certainly, this

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

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

is better than

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

Also, ranged forloop only iterates over whole containers from start to end, whilst for_eachis more flexible.

此外,远程for循环仅从头到尾遍历整个容器,同时for_each更灵活。

回答by David Rodríguez - dribeas

The for_eachloop is meant to hide the iterators (detail of how a loop is implemented) from the user code and define clear semantics on the operation: each element will be iterated exactly once.

所述for_each环路是指以隐藏来自用户代码的迭代(的环是如何实现的细节)并限定在操作清晰语义:每个元素将被正好一次迭代。

The problem with readability in the current standard is that it requires a functor as the last argument instead of a block of code, so in many cases you must write specific functor type for it. That turns into less readable code as functor objects cannot be defined in-place (local classes defined within a function cannot be used as template arguments) and the implementation of the loop must be moved away from the actual loop.

当前标准中可读性的问题是它需要一个函子作为最后一个参数而不是一个代码块,所以在许多情况下你必须为它编写特定的函子类型。这会导致代码可读性降低,因为函子对象不能就地定义(在函数中定义的本地类不能用作模板参数),并且循环的实现必须从实际循环中移开。

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
}

Note that if you want to perform an specific operation on each object, you can use std::mem_fn, or boost::bind(std::bindin the next standard), or boost::lambda(lambdas in the next standard) to make it simpler:

请注意,如果要对每个对象执行特定操作,可以使用std::mem_fn, or boost::bind(std::bind在下一个标准中), or boost::lambda( 在下一个标准中使用 lambdas ) 使其更简单:

void function( int value );
void apply( std::vector<X> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), boost::bind( function, _1 ) );
   // code
}

Which is not less readable and more compact than the hand rolled version if you do have function/method to call in place. The implementation could provide other implementations of the for_eachloop (think parallel processing).

如果您确实有函数/方法可以调用,那么它的可读性和紧凑性并不比手工版本低。该实现可以提供for_each循环的其他实现(想想并行处理)。

The upcoming standard takes care of some of the shortcomings in different ways, it will allow for locally defined classes as arguments to templates:

即将推出的标准以不同的方式解决了一些缺点,它将允许本地定义的类作为模板的参数:

void apply( std::vector<int> const & v ) {
   // code
   struct myfunctor {
      void operator()( int ) { code }
   };
   std::for_each( v.begin(), v.end(), myfunctor() );
   // code
}

Improving the locality of code: when you browse you see what it is doing right there. As a matter of fact, you don't even need to use the class syntax to define the functor, but use a lambda right there:

改进代码的局部性:当您浏览时,您会看到它在那里做什么。事实上,您甚至不需要使用类语法来定义函子,而是在那里使用 lambda:

void apply( std::vector<int> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), 
      []( int ) { // code } );
   // code
}

Even if for the case of for_eachthere will be an specific construct that will make it more natural:

即使对于这种情况,for_each会有一个特定的结构使它更自然:

void apply( std::vector<int> const & v ) {
   // code
   for ( int i : v ) {
      // code
   }
   // code
}

I tend to mix the for_eachconstruct with hand rolled loops. When only a call to an existing function or method is what I need (for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )) I go for the for_eachconstruct that takes away from the code a lot of boiler plate iterator stuff. When I need something more complex and I cannot implement a functor just a couple of lines above the actual use, I roll my own loop (keeps the operation in place). In non-critical sections of code I might go with BOOST_FOREACH (a co-worker got me into it)

我倾向于将for_each构造与手卷环混合。当我只需要调用现有的函数或方法时 ( for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )) 我会选择for_each从代码中删除大量样板迭代器内容的构造。当我需要更复杂的东西并且我无法实现仅比实际使用高几行的函子时,我会滚动自己的循环(使操作保持原位)。在代码的非关键部分,我可能会使用 BOOST_FOREACH(一位同事让我参与其中)