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:
If we are talking about efficiency and speed: caching the end iterator is unnecessary because of compiler optimizations and inlining.
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.
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.)
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.
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`.
}
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.