Is there any basis for this alternative 'for' loop syntax?

  1. Direct initialization using a constructor vs copy initialization

    These are exactly identical for ints and will generate identical code. Use whichever one you prefer to read or what your code policies are, etc.

  2. != could be faster in hardware than <

    The generated code won't actually be i < n vs i != n, it'll be like i - n < 0 vs i - n == 0. That is, you'll get a jle in the first case and a je in the second case. All the jcc instructions have identical performance (see instruction set reference and optionization reference, which just list all the jcc instructions together as having throughput 0.5).

    Which is better? For ints, probably doesn't matter performance-wise.

    Strictly safer to do < in case you want to skip elements in the middle, since then you don't have to worry about ending up with an infinite/undefined loop. But just write the condition that makes the most sense to write with the loop that you're writing. Also take a look at dasblinkenlight's answer.

  3. ++i doesn't require the compiler to keep the old value of i around, which is what i++ would do.

    Yeah that's nonsense. If your compiler can't tell that you don't need the old value and just rewrite the i++ to ++i, then get a new compiler. Those definitely will compile to the same code with identical performance.

    That said, it's a good guideline to just use the right thing. You want to increment i, so that's ++i. Only use post-increment when you need to use post-increment. Full stop.


That said, the real "new hotness" would definitely be:

for (int i : range(n)) { ... }

You are right about optimizing compilers and prefix vs. postfix ++ operator. It does not matter for an int, but it matters more when you use iterators.

The second portion of your question is more interesting:

Using != instead of < is also suspicious to me, because it makes the loop semantically different than the original version, and can lead to some rare, but interesting, bugs.

I would rephrase it as "can catch some rare, but interesting, bugs."

One simple way to argue this point was offered by Dijkstra in his book A Discipline of Programming. He pointed out that it is easier to reason about loops with stronger post-conditions than it is to reason about loops with weaker post-conditions. Since post-condition of a loop is the inverse of its continuation condition, one should prefer loops with weaker continuation conditions.

a != b is weaker than a < b, because a < b implies that a != b, but a != b does not imply that a < b. Therefore, a != b makes a better continuation condition.

In very simple terms, you know that a == b immediately after the loop with a != b is over; on the other hand, when the loop with a < b is over, all you know is that a >= b, which is not as good as knowing the exact equality.


I personally don't like the second one, I would use:

for (int i = 0; i < n; ++i); //A combination of the two :)

int i = 0 vs int i(0)

No difference whatsoever, they even compile to the same assembly instructions (no optimizations):

int main()
{
    int i = 0; //int i(0);
}

int i = 0 version:

main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, -4(%rbp)
    movl    $0, %eax
    popq    %rbp
    ret

int i(0) version:

main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, -4(%rbp)
    movl    $0, %eax
    popq    %rbp
    ret

i < n vs i != n

You are right that the != might introduce some interesting bugs:

for (int i = 0; i != 3; ++i)
{
    if (i == 2)
        i += 2; //Oops, infinite loop
    //...
}

The != comparison is mostly used for iterators, which don't define a < (or >) operator. Maybe that's what the author meant?

But here, the second version is clearly better, as it more clearly states intent than the other one (and introduces less bugs).


i++ vs ++i

For built-in types (and other trivial types), such as int, there is no difference, as the compiler will optimize the temporary return values out. Here, again, some iterators are expensive, and so the creation and destruction might be a performance hit.

But that really doesn't matter in this case, as even without optimizations they emit the same assembly output!

int main()
{
    int i = 0;
    i++; //++i;
}

i++ version:

main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, -4(%rbp)
    addl    $1, -4(%rbp)
    movl    $0, %eax
    popq    %rbp
    ret

++i version:

main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, -4(%rbp)
    addl    $1, -4(%rbp)
    movl    $0, %eax
    popq    %rbp
    ret

The two forms are in no way related to performance. What matters is how you write code in general. Follow similar patterns and focus on expressiveness and conciseness. So for the initialisation, prefer int i(0) (or better: i{0}) as this underlines that this is an initialisation, not an assignment. For the comparison, the difference between != and < is that you put lower requirements on your type. For integers there is no difference, but in general iterators might not support less than, but should always support equality. Finally, prefix increment expresses your intent better because you do not use the result.


In this code it makes no difference. But I guess what the writer is aiming for is to use the same style coding style for all of the for loops (except range-based ones, presumably).

For example if we had some other class as the loop counter type

for ( Bla b = 0; b < n; b++ )

then there are problems:

  • Bla b = 0; may fail to compile if Bla does not have an accessible copy/move constructor
  • b < n may fail to compile if Bla does not admit definiig a weak ordering, or define operator<.
  • b++ may fail to compile or have unintended side-effects, since post-increment typically returns by value

Using the writer's suggested pattern, the requirements on a loop iterator are lessened.


Note that this discussion can go on forever. We could say int i{}; is better because some types might not admit 0 as initializer. Then we could say, well what if n was not an int? It should really be decltype(n) i{}. Or in fact we should use a range-based loop which fixes all the above problems. And so on.

So at the end of the day it is still personal preference.