Advantages of std::for_each over for loop

后端 未结 21 634
遥遥无期
遥遥无期 2020-11-29 15:23

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

相关标签:
21条回答
  • 2020-11-29 15:50

    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.

    0 讨论(0)
  • 2020-11-29 15:50

    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.

    0 讨论(0)
  • 2020-11-29 15:52

    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)));
    
    0 讨论(0)
  • 2020-11-29 15:52

    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); }
    
    0 讨论(0)
  • 2020-11-29 15:53

    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.

    0 讨论(0)
  • 2020-11-29 15:54

    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, parallel 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) + lambdas:

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

    is IMO more readable than:

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

    Also this:

    for_each(bananas, [&](auto& b) { my_monkey.eat(b); );
    

    Is more concise than:

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

    But new range based for is probably the best:

     for (auto& b : bananas)
         my_monkey.eat(b);
    

    But the for_each could be useful, especially if you have several functions to call in order but need to run each method for all objects before next... 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?

    0 讨论(0)
提交回复
热议问题