Should one prefer STL algorithms over hand-rolled loops?

后端 未结 11 1386
[愿得一人]
[愿得一人] 2020-12-30 21:29

I seem to be seeing more \'for\' loops over iterators in questions & answers here than I do for_each(), transform(), and the like. Scott Meyers suggests that stl algori

相关标签:
11条回答
  • 2020-12-30 22:11

    It depends, if the algorithm doesn't take a functor, then always use the std algorithm version. It's both simpler for you to write and clearer.

    For algorithms that take functors, generally no, until C++0x lambdas can be used. If the functor is small and the algorithm is complex (most aren't) then it may be better to still use the std algorithm.

    0 讨论(0)
  • 2020-12-30 22:19

    IMO, a lot of standard library algorithms like std::for_each should be avoided - mainly for the lack-of-lambda issues mentioned by others, but also because there's such a thing as inappropriate hiding of details.

    Of course hiding details away in functions and classes is all part of abstraction, and in general a library abstraction is better than reinventing the wheel. But a key skill with abstraction is knowing when to do it - and when not to do it. Excessive abstraction can damage readability, maintainability etc. Good judgement comes with experience, not from inflexible rules - though you must learn the rules before you learn to break them, of course.

    OTOH, it's worth considering the fact that a lot of programmers have been using C++ (and before that, C, Pascal etc) for a long time. Old habits die hard, and there is this thing called cognitive dissonance which often leads to excuses and rationalisations. Don't jump to conclusions, though - it's at least as likely that the standards guys are guilty of post-decisional dissonance.

    0 讨论(0)
  • 2020-12-30 22:23

    The std::foreach is the kind of code that made me curse the STL, years ago.

    I cannot say if it's better, but I like more to have the code of my loop under the loop preamble. For me, it is a strong requirement. And the std::foreach construct won't allow me that (strangely enough, the foreach versions of Java or C# are cool, as far as I am concerned... So I guess it confirms that for me the locality of the loop body is very very important).

    So I'll use the foreach only if there is only already a readable/understandable algorithm usable with it. If not, no, I won't. But this is a matter of taste, I guess, as I should perhaps try harder to understand and learn to parse all this thing...

    Note that the people at boost apparently felt somewhat the same way, for they wrote BOOST_FOREACH:

    #include <string>
    #include <iostream>
    #include <boost/foreach.hpp>
    
    int main()
    {
        std::string hello( "Hello, world!" );
    
        BOOST_FOREACH( char ch, hello )
        {
            std::cout << ch;
        }
    
        return 0;
    }
    

    See : http://www.boost.org/doc/libs/1_35_0/doc/html/foreach.html

    0 讨论(0)
  • 2020-12-30 22:23

    I'm a big fan of the STL algorithms in principal but in practice it's just way too cumbersome. By the time you define your functor/predicate classes a two line for loop can turn into 40+ lines of code that is suddenly 10x harder to figure out.

    Thankfully, things are going to get a ton easier in C++0x with lambda functions, auto and new for syntax. Checkout this C++0x Overview on Wikipedia.

    0 讨论(0)
  • 2020-12-30 22:26

    It depends on:

    • Whether high-performance is required
    • The readability of the loop
    • Whether the algorithm is complex

    If the loop isn't the bottleneck, and the algorithm is simple (like for_each), then for the current C++ standard, I'd prefer a hand-rolled loop for readability. (Locality of logic is key.)

    However, now that C++0x/C++11 is supported by some major compilers, I'd say use STL algorithms because they now allow lambda expressions — and thus the locality of the logic.

    0 讨论(0)
  • 2020-12-30 22:26

    I’m going to go against the grain here and advocate that using STL algorithms with functors makes code much easier to understand and maintain, but you have to do it right. You have to pay more attention to readability and clearity. Particularly, you have to get the naming right. But when you do, you can end up with cleaner, clearer code, and paradigm shift into more powerful coding techniques.

    Let’s take an example. Here we have a group of children, and we want to set their “Foo Count” to some value. The standard for-loop, iterator approach is:

    for (vector<Child>::iterator iter = children.begin();
        iter != children.end();
        ++iter)
    {
        iter->setFooCount(n);
    }
    

    Which, yeah, it’s pretty clear, and definitely not bad code. You can figure it out with just a little bit of looking at it. But look at what we can do with an appropriate functor:

    for_each(children.begin(), children.end(), SetFooCount(n));
    

    Wow, that says exactly what we need. You don’t have to figure it out; you immediately know that it’s setting the “Foo Count” of every child. (It would be even clearer if we didn’t need the .begin() / .end() nonsense, but you can’t have everything, and they didn’t consult me when making the STL.)

    Granted, you do need to define this magical functor, SetFooCount, but its definition is pretty boilerplate:

    class SetFooCount
    {
    public:
        SetFooCount(int n) : fooCount(n) {}
    
        void operator () (Child& child)
        {
            child.setFooCount(fooCount);
        }
    
    private:
        int fooCount;
    };
    

    In total it’s more code, and you have to look at another place to find out exactly what SetFooCount is doing. But because we named it well, 99% of the time we don’t have to look at the code for SetFooCount. We assume it does what it says, and we only have to look at the for_each line.

    What I really like is that using the algorithms leads to a paradigm shift. Instead of thinking of a list as a collection of objects, and doing things to every element of the list, you think of the list as a first class entity, and you operate directly on the list itself. The for-loop iterates through the list, calling a member function on each element to set the Foo Count. Instead, I am doing one command, which sets the Foo Count of every element in the list. It’s subtle, but when you look at the forest instead of the trees, you gain more power.

    So with a little thought and careful naming, we can use the STL algorithms to make cleaner, clearer code, and start thinking on a less granular level.

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