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条回答
等我变得足够好
2楼-- · 2019-01-08 03:41

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

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 for loop syntax is available:

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 foreach loops than classical for loops in every recent Java or C# code I saw.

查看更多
smile是对你的礼貌
3楼-- · 2019-01-08 03:42

Aside from readability and performance, one aspect commonly overlooked is consistency. There are many ways to implement a for (or while) loop over iterators, from:

for (C::iterator iter = c.begin(); iter != c.end(); iter++) {
    do_something(*iter);
}

to:

C::iterator iter = c.begin();
C::iterator end = c.end();
while (iter != end) {
    do_something(*iter);
    ++iter;
}

with many examples in between at varying levels of efficiency and bug potential.

Using for_each, however, enforces consistency by abstracting away the loop:

for_each(c.begin(), c.end(), do_something);

The only thing you have to worry about now is: do you implement the loop body as function, a functor, or a lambda using Boost or C++0x features? Personally, I'd rather worry about that than how to implement or read a random for/while loop.

查看更多
仙女界的扛把子
4楼-- · 2019-01-08 03:46

You're mostly correct: most of the time, std::for_each is a net loss. I'd go so far as to compare for_each to goto. goto provides 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 goto in isolation tells you virtually nothing about what's it's intended to do in this situation. As a result, almost nobody in their right mind uses goto except as a last resort.

Among the standard algorithms, for_each is much the same way -- it can be used to implement virtually anything, which means that seeing for_each tells you virtually nothing about what it's being used for in this situation. Unfortunately, people's attitude toward for_each is about where their attitude toward goto was in (say) 1970 or so -- a few people 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.

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:

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:

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.

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

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

and use 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 coll to std::cout.

查看更多
爷的心禁止访问
5楼-- · 2019-01-08 03:48

With C++11 and two simple templates, you can write

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

as a replacement for for_each or a loop. Why choose it boils down to brevity and safety, there's no chance of error in an expression that's not there.

For me, for_each was always better on the same grounds when the loop body is already a functor, and I'll take any advantage I can get.

You still use the three-expression for, but now when you see one you know there's something to understand there, it's not boilerplate. I hate boilerplate. I resent its existence. It's not real code, there's nothing to learn by reading it, it's just one more thing that needs checking. The mental effort can be measured by how easy it is to get rusty at checking it.

The templates are

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); }
查看更多
闹够了就滚
6楼-- · 2019-01-08 03:49

its very subjective, some will say that using for_each will make the code more readable, as it allows to treat different collections with the same conventions. for_each itslef is implemented as a loop

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.

查看更多
Animai°情兽
7楼-- · 2019-01-08 03:51

Easy: for_each is 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(a.begin(), a.end(), a_item_handler);

is better than

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

Also, ranged for loop only iterates over whole containers from start to end, whilst for_each is more flexible.

查看更多
登录 后发表回答