Caching the end iterator - Good idea or Bad Idea?

你离开我真会死。 提交于 2019-12-20 11:38:12

问题


Generally speaking is it a good idea to cache an end iterator (specifically STL containers) for efficiency and speed purposes? such as in the following bit of code:

std::vector<int> vint;
const std::vector<int>::const_iterator end = vint.end();
std::vector<int>::iterator it = vint.begin();

while (it != end)
{
   ....
   ++it;
}

Under what conditions would the end value be invalidated? would erasing from the container cause end to be invalidated in all STL containers or just some?


回答1:


In the simple case of a vector, the end iterator will change when you add or remove elements from the container; though, it's usually safest to assume that if you mutate the container while iterating over it, all iterators to it become invalid. Iterators may be implemented differently in any given STL implementation.

With regard to caching the end iterator -- it's certainly valid to cache it, but to find out if it is actually faster in your case, the best bet is for you to profile your code and see. While retrieving the end iterator from a vector is likely a fast implementation with a recent STL library and compiler, I have worked on past projects where caching the end iterator gave us a significant speed boost. (This was on the PlayStation 2, so do take with a grain of salt.)




回答2:


If we are talking about efficiency and speed: caching the end iterator is unnecessary because of compiler optimizations and inlining.




回答3:


Erasing from a container over which you are currently iterating is always a bad idea. The actual caching of your end iterator is not going to change that.

h.




回答4:


Generally speaking is it a good idea to cache an end iterator (specifically STL containers) for efficiency and speed purposes?

If you use the STL container algorithms the caching of the end iterator is happens anyway (as you pass the result of container.end() as a parameter).

If you modify the memory of the container (insert/remove elements) it's a bad bad idea.

Also, caching for efficiency rarely makes much sense: in most cases the end() is inlined by the compiler, and when it is not, it is very probable that your efficiency doesn't hang on the end() result being cached. YMMV though.




回答5:


The invalidation rules (for iterators) is defined very explicitly for each type of container. I find the SGI site very useful http://www.sgi.com/tech/stl/table_of_contents.html

Specifically for vectors I find:

[5] A vector's iterators are invalidated when its memory is reallocated. Additionally, inserting or deleting an element in the middle of a vector invalidates all iterators that point to elements following the insertion or deletion point. It follows that you can prevent a vector's iterators from being invalidated if you use reserve() to preallocate as much memory as the vector will ever use, and if all insertions and deletions are at the vector's end.




回答6:


I often use this style for iterating containers:

// typedef std::vector<Person> Persons;
Persons::iterator it = persons.begin(), end = persons.end();
for (; it != end; ++it)
{
    Person & person = *it;
    // ...
}

Erasing an element from a vector invalidates all iterators after the erased position.

I'm not certain about the other container types. In any case I think it would be safe to assume that all iterators become invalid after an erase. If you really need very specific information then you can always look it up. I rarely need this because because of my rather conservative coding style.




回答7:


In general it should not matter if you cache the end iterator. If you feel it does matter, then you should already be using a profiler on your code and would be able to profile both variations. I'd suspect it could differ depending upon the container type - but a profiler would be the only way to know for sure given your compiler, optimizations and STL vendor.




回答8:


It really, really depends of what are you doing in the ... code.

If the compiler can prove that vint.end() is not going to change then it might not matter, but you are at the mercy of the compiler optimizations then and how clear is the ... code.

Your approach is the one that helps the compiler the most, you are promising that the end iterator will not be invalidated and that you are not going to modify the element in end (which is invalid any way). You cannot be more explicit than this about what you promise you will do in ....

It is 2019, and for-range loops are basically equivalent to your code: https://en.cppreference.com/w/cpp/language/range-for

{
    auto && __range = range_expression ;
    auto __begin = begin_expr ;
    auto __end = end_expr ;
    for ( ; __begin != __end; ++__begin) {

        range_declaration = *__begin;
        loop_statement

    }
}

which incidentally means that if you were not really interested in it, but in *it, you can put an end (no pun intended) to the dilemma and just write:

std::vector<int> vint;
for(auto&& e : vint)
{
   .... // use `e` instead of `*it`.
}


来源:https://stackoverflow.com/questions/3084109/caching-the-end-iterator-good-idea-or-bad-idea

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!