Question

Which of the following is better and why? (Particular to c++)

a.

int i(0), iMax(vec.length());//vec is a container, say std::vector
for(;i < iMax; ++i)
{
  //loop body
}

b.

for( int i(0);i < vec.length(); ++i)
{
  //loop body
}

I have seen advice for (a) because of the call to length function. This is bothering me. Doesn't any modern compiler do the optimization of (b) to be similar to (a)?

Was it helpful?

Solution

Example (b) has a different meaning to example (a), and the compiler must interpret it as you write it.

If, (for some made-up reason that I can't think of), I wrote code to do this:

for( int i(0);i < vec.length(); ++i)
{
    if(i%4 == 0)
       vec.push_back(Widget());
}

I really would not have wanted the compiler to optimise out each call to vec.length(), because I would get different results.

OTHER TIPS

I like:

for (int i = 0, e = vec.length(); i != e; ++i)

Of course, this would also work for iterators:

for (vector<int>::const_iterator i = v.begin(), e = v.end(); i != e; ++i)

I like this because it's both efficient (calling end() just once), and also relatively succinct (only having to type vector<int>::const_iterator once).

I'm surprised nobody has said the obvious:

In 99.99% of cases, it doesn't matter.

Unless you are using some container where calculating size() is an expensive operation, it is unfathomable that your program will go even a few nanoseconds slower. I would say stick with the more readable until you profile your code and find that size() is a bottleneck.

There are two issues to debate here:

  1. The variable scope
  2. The end condition re-evaluation

Variable scope

Normally, you wouldn't need the loop variable to be visible outside of the loop. That's why you can declare it inside the for construct.

End condition re-evaluation

Andrew Shepherd stated it nicely: it means something different to put a function call inside the end condition:

for( vector<...>::size_type i = 0; i < v.size(); ++i ) { // vector size may grow.
   if( ... ) v.push_back( i ); // contrived, but possible
}

// note: this code may be replaced by a std::for_each construct, the previous can't.
for( vector<...>::size_type i = 0, elements = v.size(); i != elements; ++i ) {
}

Why is it bodering you? Those two alternatives dont see to be doing the same. One is doing a fixed number of iterations, while the other is dependant on the loops body.

Another alternative colud be

for (vector<T>::iterator it=vec.begin();it!=vec.end();it++){
 //loop body
}

Unless you need the loop variable outside the loop, the second approach is preferable.

Iterators will actually give you as good or better performance. (There was a big comparison thread on comp.lang.c++.moderated a few years back).

Also, I would use

int i = 0;

Rather than the constructor like syntax you're using. While valid, it's not idiomatic.

Somewhat unrelated:

Warning: Comparison between signed and unsigned integer.

The correct type for array and vector indices is size_t.

Strictly speaking, in C++ it is even std::vector<>::size_type.

Amazing how many C/C++ developers still get this one wrong.

Let's see on the generated code (I use MSVS 2008 with full optimization).

a.

int i(0), iMax(vec.size());//vec is a container, say std::vector
for(;i < iMax; ++i)
{
  //loop body
}

The for loop produces 2 assembler instructions.

b.

for( int i(0);i < vec.size(); ++i)
{
  //loop body
}

The for loop produces 8 assembler instructions. vec.size() is successfully inlined.

c.

for (std::vector<int>::const_iterator i = vec.begin(), e = vec.end(); i != e; ++i)
{
  //loop body
}

The for loop produces 15 assembler instructions (everything is inlined, but the code has a lot of jumps)

So, if your application is performance critical use a). Otherwise b) or c).

It should be noted that the iterator examples:

for (vector<T>::iterator it=vec.begin();it!=vec.end();it++){
 //loop body
}

could invalidate the loop iterator 'it' should the loop body cause the vector to reallocate. Thus it is not equivalent to

for (int i=0;i<vec.size();++i){
 //loop body
}

where loop body adds elements to vec.

Simple question: are you modifying vec in the loop?

answer to this question will lead to your answer too.

jrh

It's very hard for a compiler to hoist the vec.length() call in the safe knowledge that it's constant, unless it gets inlined (which hopefully it often will!). But at least i should definitely be declared in the second style "b", even if the length call needs to be "manually" hoisted out of the loop!

This one is preferable:

typedef vector<int> container; // not really required,
                               // you could just use vector<int> in for loop

for (container::const_iterator i = v.begin(); i != v.end(); ++i)
{
    // do something with (*i)
}
  • I can tell right away that the vector is not being updated
  • anyone can tell what is happening here
  • I know how many loops
  • v.end() returns pointer one past the last element so there's no overhead of checking size
  • easy to update for different containers or value types

(b) won't calculate/call the function each time.

-- begin excerpt ----

Loop Invariant Code Motion: GCC includes loop invariant code motion as part of its loop optimizer as well as in its partial redundancy elimination pass. This optimization removes instructions from loops, which compute a value which does not change throughout the lifetime of a loop.

--- end excerpt --

More optimizations for gcc:

https://www.in.redhat.com/software/gnupro/technical/gnupro_gcc.php3

Why not sidestep the issue entirely with BOOST_FOREACH

#include <boost/foreach.hpp>

std::vector<double> vec;

//...

BOOST_FOREACH( double &d, vec)
{
    std::cout << d;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top