Reputation: 97
I need the opinion of stack overflow to settle something that has been sitting in the back of my head about for loop efficiency. Now, when I began programming, "getting it to work" was a big priority and most of my for loops looked like this.
for (int i = 0; i < N; i++) {;}
Then I was tought, especially with C++ development that pre-increment saves you some calls, and considering that would be N calls and it doesn't alter the readability I got into the habit of.
for (int i = 0; i < N; ++i) {;}
This was good enough for a while, but focusing on the readability, and after reading some of Code Complete by Steve McConnell, I arrived at this.
for (int loop_index = 0; loop_index < loop_count; ++loop_index) {;}
And these variables change based on the context of the code. Then I read some of Effective C++ about built in types constructors and assignment. And basically that the difference between
int i = 42;
and
int i(42);
Is that the former one calls the constructor and the assignment operator while the latter only the constructor. So I got that into my routine whilst coding. So my question, is this the most efficient and readable way of writing a for loop:
for (int loop_index(0); loop_index < loop_counter; ++loop_index) {;}
Upvotes: 2
Views: 280
Reputation: 73590
If you're using built in types, or most iterators, all of these will compile to the same code.
So I'd stick with the one that is most idiomatic to C++.
for (int i = 0; i < N; ++i) {;}
or if you want to be wordy ( but i and k are traditionally loop indices )
for (int loop_index = 0; loop_index < loop_count; ++loop_index) {;}
As evidence for my initial claim: These two pieces of code generate identical assembly code. ( g++ 4.0.1 on OS X compiled with g++ -Os -S test.cpp
)
void f(int); // So that smart compilers can't optimise away the loop entirely.
void g()
{
int N=100;
// for( int i=0; i<N; i++)
for (int i(0); i < N; ++i)
{
f(i);
}
}
For completeness, the generated code is:
.globl __Z1gv
__Z1gv:
LFB2:
pushl %ebp
LCFI0:
movl %esp, %ebp
LCFI1:
pushl %esi
LCFI2:
xorl %esi, %esi
subl $20, %esp
LCFI3:
L2:
movl %esi, (%esp)
incl %esi
call L__Z1fi$stub
cmpl $100, %esi
jne L2
addl $20, %esp
popl %esi
leave
ret
Upvotes: 1
Reputation: 64923
Efficient? No, it most likely will be the same, as most compilers will optimize that post increment and initialization for int and simple pointers. And as to whether loop_index is more readable than i, IMO, it's less readable for simple counters.
If it's an iterator though, it's different story, compilers can't always optimize overloaded post increment or double initialization of iterators. I also prefer using the iterated object's names for iterator, as that reflects what's being used better, using the object's name looks more natural when using the arrow -> operator to dereference the iterator.
Upvotes: 0
Reputation: 15824
If you're not using iterators, it shouldn't matter, because compiler will translate this to the same code.
Addendum: If you're using iterators:
for(vector<int>::iterator i = a.begin(); i != a.end(); ++i)
Then ++i is i.operator++() and i++ is i.operator++(int) which can mean two different things.
BTW. You can measure performance if this is a problem.
Upvotes: 2
Reputation: 2436
While others have covered post- vs pre-increments and initializations quite well, I'd like to add that using int i
in most cases is more readable than int loop_index
, because that's what everyone else is using and expecting.
The only cases when you may want to call it something else is when you have multiple loops, and complicated interactions between the loop indexes. For example
for (int row = 0; row < rows; row++) {
for (int column = 0; column < columns; column++) {
stuff[row * columns + column] = calc_stuff(row, column);
}
}
Upvotes: 0
Reputation: 34654
Actually,
for (int i = 0; i < N; i++) {;}
is fine. i
is an int
, so the compiler will optimise the post-increment away anyway. Whether you initialise an int
with int i(42);
or with int i = 42;
is also a question of taste.
I would also call the iterator i
, instead of loop_index
. The former is ubiquitously understood, while the latter is strange.
If you are dealing with iterators, however, the picture changes. Chances are it++
will still be optimised to ++it
, but here, I'd rather use pre-increment.
Upvotes: 5
Reputation: 227548
First of all, your last point is not correct. This line
int i = 42;
does copy initialization (see this related GoTW). There is no assignment there. For assignment you would need
int i;
i = 42;
Second, when dealing with builtins such as integer types, there will probably be no difference in the code produced by the compiler. You should profile the different options and see.
Upvotes: 2