Advantages of std::for_each over for loop

2019-01-08 03:28发布

Are there any advantages of std::for_each over for loop? To me, std::for_each only seems to hinder the readability of code. Why do then some coding standards recommend its use?

20条回答
Juvenile、少年°
2楼-- · 2019-01-08 04:00

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.

Update 1:

Most importantly, it helps you go beyond for_each vs. 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.

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.

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

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

Or with C++x11 lambdas:

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

is IMO more readable than:

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

Also this (or with lambdas, see others):

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?

查看更多
▲ chillily
3楼-- · 2019-01-08 04:00

for_each is 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_each without having to update the iteration code. You need to consider that there are other containers in the world than std::vector and plain old C arrays to see the advantages of for_each.

The major drawback of for_each is that it takes a functor, so the syntax is clunky. This is fixed in C++0x with the introduction of 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.

查看更多
Fickle 薄情
4楼-- · 2019-01-08 04:00

Mostly you'll have to iterate over the whole collection. Therefore I suggest you write your own for_each() variant, taking only 2 parameters. This will allow you to rewrite Terry Mahaffey's example as:

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

I think this is indeed more readable than a for loop. However, this requires the C++0x compiler extensions.

查看更多
狗以群分
5楼-- · 2019-01-08 04:00

for_each allow us to implement Fork-Join pattern . Other than that it supports fluent-interface.

fork-join pattern

We can add implementation gpu::for_each to use cuda/gpu for heterogeneous-parallel computing by calling the lambda task in multiple workers.

gpu::for_each(users.begin(),users.end(),update_summary);
// all summary is complete now
// go access the user-summary here.

And gpu::for_each may wait for the workers work on all the lambda-tasks to finish before executing the next statements.

fluent-interface

It allow us to write human-readable code in concise manner.

accounts::erase(std::remove_if(accounts.begin(),accounts.end(),used_this_year));
std::for_each(accounts.begin(),accounts.end(),mark_dormant);
查看更多
Lonely孤独者°
6楼-- · 2019-01-08 04:02

The for_each loop 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.

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::bind in the next standard), or boost::lambda (lambdas in the next standard) to make it simpler:

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_each loop (think parallel processing).

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:

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_each there will be an specific construct that will make it more natural:

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

I tend to mix the for_each construct 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_each construct 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)

查看更多
甜甜的少女心
7楼-- · 2019-01-08 04:06

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_FOREACH and C++0x's range-based for clearer:

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)));
查看更多
登录 后发表回答