views:

413

answers:

6

What are the Advantages/Drawbacks of these two ways of iterating through a container / which one do you prefer and why:

for (MyClass::iterator i = m.begin(), e = m.end() ; i != e ; i++)
{
 // ...
}

or

for (MyClass::iterator i = m.begin() ; i != m.end() ; i++)
{
 // ...
}

Subsidiary question: i++ or ++i? Why?

+3  A: 

Unless you have optimizations turned off, both are equivalent. As for i++ or ++i, ++i is more efficient because it does not involve a temporary value.

Tanveer Badar
A: 

I always do the second one actually, although I do worry sometimes if multiple calls to end slows things down at all. I was under the impression this would be optimised, but don't know that for sure.

And ++i definitely. It's never slower than i++, if anything it's faster.

Ray Hidayat
+1  A: 

The first one is faster because end() isn't called on every iteration. And no, the optimizer can't easily cache that for you, because it doesn't know whether the size of the container has changed in this iteration (and hence the end moved). This also applies to a const container due to the aliasing problem.

i++ returns a copy of i, then increments. ++i increments, then returns the incremented value. Hence, when you are discarding the return value, use ++i because it needs to do less work (no copying). The optimizer is quite likely to fix an inlined i++ call so it's as fast as ++i but don't rely on that.

Me? I use

for(int i = 0; i < m.size(); i++) {
    // ... do something with m[i]
}

Because it's the shortest and most clear. Why int and not MyClass::size_type? Because it's simpler and I have never had to worry about the edge cases so far. Why i++? Because for basic types it's always optimized into ++i, and it's less confusing for coworkers. As a bonus, I can use i as a numeric value as well. With an iterator, I'd have to keep a separate counter, or use std::distance.

obecalp points out that this doesn't work with half of the standard containers, like list and map. Indeed those require using a proper iterator. Relatedly, you should always use an iterator when writing generic code.

You're assuming it's a container that supports operator[], map/set, list and many others doesn't support operator[].
obecalp
Ah, yes! The one time I iterated over a map, I used a map<T>::iterator. And I never used a list so far but I'd use an iterator for it as well.
You should be using size_t and not int. mixing signed and unsigned types for < and > is a great way to accidentally add an infinite-loop edge case.
Tom
Tom: That'd make sense were it not for unsigned's tendency to silently screw up computations due to promotion rules. Consider "unsigned int i = 6". Calculate "i / -2", "i % -2" and "i * -2" in your head. Now punch them into a compiler. Did you get what you expected? :) ... I never ever use unsigned.
Why would you do multiplication on an iterator index?
Steve Jessop
because i want to init my 3 input boxes with { -2, -4, -6 } for example.
@Iraimbilanja - You *are* using unsigned (size() returns size_t). Hence, my recommendation to use "size_t i" so promotion rules don't screw you up.
Tom
I know size() is unsigned. This only matters in the "i < m.size()" comparison, and there it is fine because I'm assuming that i is positive.
+2  A: 

For normal stl iterators there's not a great deal of difference however if you collections are complex and asking for end is expensive then only asking for the end once may be faster.

Similarly for ++i vs i++, ++i can be a more expensive operation when the iterator is a complex class (rather than just a pointer as in the stl iterators) with i++ what's happening is that it's incrementing the iterator but returning a copy of the iterator in it's previous state. for ++i it's returning the iterator in it's current state so can just return a reference to itself.

It's usually best to only optimize your code when your profiler identifies that there's a problem there - it's better to keep the code as easily readable as possible.

Tom
I wonder if there's a word for "scattering small slowdowns throughout your code, so they accumulate and considerably affect performance, yet are undetectable by a profiler".
The word is "java" :o)
Tom
+3  A: 

If the iterator is non-trivial (ie. not a pointer), ++i is definitely faster as it doesn't involves a copy to a temporary, which may or may not be optimized out.

The first form is a little faster but could be wrong if you erase or insert things in the loop.

For simple iteration over a container I use

#define foreach BOOST_FOREACH // in some header

foreach(MyType &element, any_container) {
  // deal with element
}

most of the time for succinctness and clarity.

obecalp
Cool, I didn't know about BOOST_FOREACH. I'm discovering more and more useful things in boost. Will start using this one too. :)
A: 

Boost.Foreach introduces a nice way:

#define foreach         BOOST_FOREACH
// ...
Container<Item> container;
// ...
foreach (Item item, container) {
  // do some stuff with the item
}
J.F. Sebastian