I see a lot of c++ code that looks like this:
for( const_iterator it = list.begin(),
const_iterator ite = list.end();
it != ite; ++it)
<
I always preferred the first one. Though with inline functions, compiler optimizations and relatively smaller container size ( in my case it's normally max 20-25 items) it really does not make any large difference with respect to performace.
const_iterator it = list.begin();
const_iterator endIt = list.end();
for(; it != endIt ; ++it)
{//do something
}
But recently I am using more of std::for_each wherever it's possible. Its optimized looping which helps to make the code look more readable than other two.
std::for_each(list.begin(), list.end(), Functor());
I will use the loop only when std::for_each
cannot be used. (for ex: std::for_each
does not allow you to break the loop unless an exception is thrown).
The compiler might be able to optimize the second into the first, but that assumes that the two are equivalent, i.e. end() actually is constant. A slightly more problematic issue is that the compiler may be unable to deduce that the end iterator is constant due to possible aliasing. However, assuming that the call to end() is inlined, the difference is just a memory load.
Note that this assumes that the optimizer is enabled. If the optimizer is not enabled, as is often done in debug builds, then the second formulation will involve N-1 more function calls. In current versions of Visual C++, debug builds will also incur additional hits due to function prolog/epilog checks and heavier debug iterators. Therefore, in STL heavy code, defaulting to the first case can prevent code from being disproportionately slow in debug builds.
Insertion and removal within the loop are a possibility, as others have pointed out, but with this style of loop I find that unlikely. For one thing, node-based containers -- list, set, map -- don't invalidate end() on either operation. Second, the iterator increment frequently has to be moved in-loop to avoid invalidation problems:
// assuming list -- cannot cache end() for vector iterator it(c.begin()), end(c.end()); while(it != end) { if (should_remove(*it)) it = c.erase(it); else ++it; }
Thus, I consider a loop that claims to call end() for mutate-during-loop reasons and still has ++it in the loop header to be suspect.
In theory the compiler could optimise the second version into the first (assuming the container doesn't change during the loop, obviously).
In practice, I've found several similar cases when profiling time-critical code where my compiler has totally failed to hoist invariant calculations out of loop conditions. So while the slightly more concise version is fine in most cases, I don't rely on the compiler doing sensible things with it for a case where I'm really concerned about performance.
The first one will probably almost always be faster, but if you think this will make a difference, always profile first to see which is faster and by how much.
The compiler will probably be able to inline the call to end()
in both cases, although if end()
is sufficiently complicated, it may opt not to inline it. However, the key optimization is whether or not the compiler can perform loop-invariant code motion. I would posit that in most cases, the compiler can't be certain that the value of end()
won't change during the iteration of the loop, in which case it has no choice but to call end()
after each iteration.
You can make the first version more concise and get the best of both:
for( const_iterator it = list.begin(), ite = list.end();
it != ite; ++it)
P.S. The iterators aren't const, they're iterators to a const reference. There's a big difference.