Is there any reason for this alternative for loop syntax?

I met a set of slides for discussing rant in C ++. There were interesting interesting tidbits, but slide 8 seemed to me. Its contents were approximately:

Changing styles

Old and busted:

for (int i = 0; i < n; i++) 

New heat:

 for (int i(0); i != n; ++i) 

I had never seen a for loop using the second form before, so the requirement that it was "New Heat" interested me. I can understand some reasons:

  • Direct initialization using constructor versus copy initialization
  • != may be faster in equipment than <
  • ++i does not require the compiler to maintain the old value of i , which would be i++ .

I would suggest that this is premature optimization, as modern optimizing compilers will compile these two to the same instructions; in any case, the latter is worse because it is not a “normal” for loop. Using != Instead of < also suspicious to me, because it makes the loop semantically different from the original version and can lead to some rare but interesting errors.

Was there any point where the "New Hotness" version of the for loop was popular? Is there a reason to use this version these days (2016+), for example. unusual types of loop variables?

+44
c ++ for-loop
Sep 14 '16 at 15:35
source share
7 answers
  • Direct initialization using constructor versus copy initialization

    They are identical to int and will generate identical code. Use what you prefer to read, or what your code policies, etc.

  • != may be faster in equipment than <

    The generated code will not actually be i < n vs i != n , it will be like i - n < 0 vs i - n == 0 . That is, you get jle in the first case and je in the second case. All jcc have the same performance (see the link to the set of instructions and the link to the options , which list all jcc along with a bandwidth of 0.5).

    What's better? For int s, it probably doesn't matter in terms of performance.

    It is strictly safer to do < if you want to skip items in the middle, since you don’t have to worry about ending up with an endless loop / w 90>. But just write a condition that makes sense to write with the loop you write. See also dasblinkenlight answer .

  • ++i does not require the compiler to keep the old value of i , which will be i++ .

    Yes, this is nonsense. If your compiler cannot say that you do not need the old value and just rewrite i++ to ++i , then get a new compiler. They will definitely compile with the same code with the same performance.

    However, it is a good recommendation to just use the right thing. You want to increase i to ++i . Use only post-growth when you need to use post-growth. Full stop.




However, the real "new heat" will definitely be:

 for (int i : range(n)) { ... } 
+75
Sep 14 '16 at 15:50
source share

You are right in optimizing compiler and prefix and postfix ++ operators. It does not matter for int , but it is important when you use iterators.

The second part of your question is more interesting:

Using != Instead of < also suspicious to me, because it makes the loop semantically different from the original version and can lead to some rare but interesting errors.

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

One easy way to argue for this point was suggested by Dijkstra in his book Discipline of Programming . He noted that it is easier to talk about loops with stronger post-conditions than to talk about loops with weaker post-conditions. Since the post-condition of the cycle is the opposite of its continuation condition, contours with weaker continuation conditions should be preferred.

a != b weaker than a < b , because a < b implies that a != b , but a != b does not mean that a < b . Therefore, a != b provides a better continuation condition.

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

+22
Sep 14 '16 at 15:49
source share

I personally do not like the second, I would use:

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



int i = 0 vs int i(0)

There is no difference whatsoever, they do not even compile with the same assembly instructions (without optimization):

 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 correct that != May introduce some interesting errors:

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

The comparison != mainly used for iterators that do not define the < (or > ) operator. Perhaps this is what the author had in mind?

But here the second version is clearly better, since it more clearly states its intention than the other (and introduces fewer errors).




i++ vs ++i

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

But this really does not matter in this case, since even without optimization they produce the same combined 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 
+13
Sep 14 '16 at 15:53
source share

These two forms are in no way associated with performance. What matters is how you write code in general. Follow similar patterns and focus on expressiveness and conciseness. Therefore, for initialization, we prefer int i (0) (or better: i {0}), since this emphasizes that this is initialization, not the purpose. For comparison, the difference between = = <is that you set lower requirements for your type. There is no difference for integers, but in general iterators can support less than, but must always maintain equality. Finally, incrementing the prefix expresses your intention better because you are not using the result.

+7
Sep 14 '16 at 19:36
source share

In this code, it does not matter. But I assume that the author aims to use the same style of style for all for loops (with the exception of range-based, presumably).

For example, if we had some other class as a type of loop counter

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

then there are problems:

  • Bla b = 0; may not compile if Bla does not have a copy / move constructor available
  • b < n may not compile if Bla does not allow the definition of a weak order or defines operator< .
  • b++ may not compile or have unintended side effects, since post-increment usually returns

Using the template proposed by the author, the requirements for the loop iterator are reduced.




Please note that this discussion may go on forever. We could say that int i{}; better because some types may not allow 0 as an initializer. Then we could say that if n not int ? It really should be decltype(n) i{} . Or in fact, we should use a range-based loop that eliminates all of the above problems. And so on.

So, at the end of the day, it's still a personal preference.

+3
Sep 15 '16 at 0:31
source share

i! = n

Honestly, slide 8 lost me right there, and you are right with the suspicion that something might not be right.

Besides the high probability that the modern compiler will have such kinds of loops optimized as carefully as theoretically possible for current processors, encouraging the programmer to write less reliable code to “help the optimizer” for any reason is simply oldfashioned and has no place at all modern world, IMO. The real cost of software is human time these days, not processor time, for 99.99 ...% of all projects.

At the meta level, truths have no place in coding guidelines. A slide establishing an encoding agreement without providing objective and well-reasoned reasons for this is useless. Note that I would be fine with a reason such as “we do it this way because we want to choose one style instead of many,” I don’t need a technical reason - just for some reason. I can then decide if I accept the arguments for the coding guide or not (and thus accept the guide).

+3
Sep 15 '16 at 11:14
source share

It is stylistically better to use postfix increment / decrement in a C-style for loop when possible,

 for (i = 0; i < n; i++) 

but not

 for (i = 0; i < n; ++i) 

Using the postfix operator, a loop variable i appears on the left side of all three expressions, which makes it easier to read the code. In addition, if you need to execute any value other than 1, you must write it with i on the left ...

 for (i = 0; i < n; i += 2) 

therefore, for consistency, this should also be done this way when you use shorthand ++ for += 1 . (As stated in the comments, it would be easier for us to read += 1 than ++ if we hadn’t had 45 years of training teaching us ++ .)

When using such fundamental types as int , the prefix / postfix increment will not have a difference in performance; as other answers showed, the compiler will generate exactly the same code anyway. With iterators, the optimizer needs to do more work to fix this, but I don’t think there is any excuse for the 11th-era C ++ compiler that does not deal with the postfix operator ++ as effectively as the operator ++ prefix. when they are used solely for their side effects.

(Of course, if you are stuck with an iterator that only supports the ++ prefix, then you write it that way.)

0
Sep 15 '16 at 0:22
source share



All Articles