Advantages of std::for_each over for loop

后端 未结 21 631
遥遥无期
遥遥无期 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:40

    for is for loop that can iterate each element or every third etc. for_each is for iterating only each element. It is clear from its name. So it is more clear what you are intending to do in your code.

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

    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.

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

    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.

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

    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)

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

    For loop can break; I dont want to be a parrot for Herb Sutter so here is the link to his presentation: http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T Be sure to read the comments also :)

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

    I used to dislike std::for_each and thought that without lambda, it was done utterly wrong. However I did change my mind some time ago, and now I actually love it. And I think it even improves readability, and makes it easier to test your code in a TDD way.

    The std::for_each algorithm can be read as do something with all elements in range, which can improve readability. Say the action that you want to perform is 20 lines long, and the function where the action is performed is also about 20 lines long. That would make a function 40 lines long with a conventional for loop, and only about 20 with std::for_each, thus likely easier to comprehend.

    Functors for std::for_each are more likely to be more generic, and thus reusable, e.g:

    struct DeleteElement
    {
        template <typename T>
        void operator()(const T *ptr)
        {
            delete ptr;
        }
    };
    

    And in the code you'd only have a one-liner like std::for_each(v.begin(), v.end(), DeleteElement()) which is slightly better IMO than an explicit loop.

    All of those functors are normally easier to get under unit tests than an explicit for loop in the middle of a long function, and that alone is already a big win for me.

    std::for_each is also generally more reliable, as you're less likely to make a mistake with range.

    And lastly, compiler might produce slightly better code for std::for_each than for certain types of hand-crafted for loop, as it (for_each) always looks the same for compiler, and compiler writers can put all of their knowledge, to make it as good as they can.

    Same applies to other std algorithms like find_if, transform etc.

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