Can end() be a costly operation for stl containers

后端 未结 7 1951
囚心锁ツ
囚心锁ツ 2020-12-16 12:16

On https://doc-snapshots.qt.io/qtcreator-extending/coding-style.html it is recommended to write for loops like the following:

Container::iterator end = large         


        
相关标签:
7条回答
  • 2020-12-16 12:25

    The C++11 standard (§ 23.2.1) mandates that end has O(1) complexity, so a conforming implementation would have the same performance characteristics for both versions.

    That said, unless the compiler can prove that the return value of end will never change then pulling end out of the loop might be faster by some constant quantity (as Steve Jessop comments, there are lots of variables that can influence whether this is true or not).

    Still, even if in one particular case there is absolutely no performance difference, pulling such tests out of the loop is a good habit to get into. An even better habit to get into is to utilize standard algorithms as @pmr says, which sidesteps the issue entirely.

    0 讨论(0)
  • 2020-12-16 12:28

    In fact, the end() method is inline. The 2nd not call it every time, I don't think end() gives any performance lag.

    0 讨论(0)
  • 2020-12-16 12:31

    Depends on the implementation, but I don't think end() gives that much of a performance lag.

    0 讨论(0)
  • 2020-12-16 12:36

    std::vector.end() (for example) return an iterator by value. In the second loop you create an object at every loop. The coding standard is telling you to do not create object if you don't need to. The compiler may be smart and optimize the code for you, however this is not guarantee. A much better solution is using stl algorithms. They are already optimized and your code will be more readable. Beware the two loops are equivalent only if you do not modified the collection.

    P.S. it is very likely that the difference in performance is very minimal

    0 讨论(0)
  • 2020-12-16 12:36

    For anyone reading this now, the question has become somewhat of a moot point with C++11.

    I wasn't sure whether this response qualifies as an answer, because it doesn't actually address the point of the question. But I do think it's valid to point out that the problem raised here will be seldom encountered in practice for a C++11 programmer, and I certainly would have found this response useful a few years ago. This response is therefore aimed at the reader who simply wants to know the best way of iterating through all elements in a STL container (vector, list, deque, etc.).

    Assuming that the OP wanted access to each element in the container, we can easily sidestep the entire question of whether defining end is sufficiently faster than calling Container::end() by writing a range-based for loop:

    Container container; // my STL container that has been filled with stuff
    
    // (note that you can replace Container::value_type with the value in the container)
    
    // the standard way
    for (Container::value_type element : container) {
        // access each element by 'element' rather than by '*it'
    }
    
    // or, if Container::value_type is large
    Container container; // fill it with something
    for (Container::value_type& element : container) {
        //
    }
    
    // if you're lazy
    Container container; // fill it with something
    for (auto element : container) {
        //
    }
    

    The OP has asked whether the trade-off between brevity of simply comparing it to Container::end() at every iteration and the performance of declaring a variable end and comparing that at each step instead is worth it. Since range-based for loops provide a simple, easy to write and easy to read alternative that also happens to, internally, declare an end iterator rather than calling the Container::end() method at every step, the number of cases where we need to dwell on this question has been reduced to a limited number of cases.

    As per cppreference.com, the range-based for loop will produce code with the same side-effects as the following:

    {
      auto && __range = range_expression ; 
      for (auto __begin = begin_expr,
            __end = end_expr; 
          __begin != __end; ++__begin) { 
        range_declaration = *__begin; 
        loop_statement 
      } 
    } 
    
    0 讨论(0)
  • 2020-12-16 12:47

    This is less about end being costly and more about the ability of a compiler to see that end will not change through a side effect in the loop body (that it is a loop invariant).

    The complexity of end is required to be constant by the standard. See table 96 in N3337 in 23.2.1.

    Using standard library algorithms circumvents the whole dilemma nicely.

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