I almost never see a for
loop like this:
for (int i = 0; 5 != i; ++i)
{}
Is there a technical reason to use >
Using relational comparisons in such cases is more of a popular habit than anything else. It gained its popularity back in the times when such conceptual considerations as iterator categories and their comparability were not considered high priority.
I'd say that one should prefer to use equality comparisons instead of relational comparisons whenever possible, since equality comparisons impose less requirements on the values being compared. Being EqualityComparable is a lesser requirement than being LessThanComparable.
Another example that demonstrates the wider applicability of equality comparison in such contexts is the popular conundrum with implementing unsigned
iteration down to 0
. It can be done as
for (unsigned i = 42; i != -1; --i)
...
Note that the above is equally applicable to both signed and unsigned iteration, while the relational version breaks down with unsigned types.
In addition to the various people who have mentioned that it mitigates risk, it also reduces the number of function overloads necessary to interact with various standard library components. As an example, if you want your type to be storable in a std::set
, or used as a key for std::map
, or used with some of the searching and sorting algorithms, the standard library usually uses std::less
to compare objects as most algorithms only need a strict weak ordering. Thus it becomes a good habit to use the <
comparisons instead of !=
comparisons (where it makes sense, of course).
There is no problem from a syntax perspective, but the logic behind that expression 5!=i
is not sound.
In my opinion, using !=
to set the bounds of a for loop is not logically sound because a for loop either increments or decrements the iteration index, so setting the loop to iterate until the iteration index becomes out of bounds (!=
to something) is not a proper implementation.
It will work, but it is prone to misbehavior since the boundary data handling is lost when using !=
for an incremental problem (meaning that you know from the start if it increments or decrements), that's why instead of !=
the <>>==>
are used.
The loop condition is an enforced loop invariant.
Suppose you don't look at the body of the loop:
for (int i = 0; i != 5; ++i)
{
// ?
}
in this case, you know at the start of the loop iteration that i
does not equal 5
.
for (int i = 0; i < 5; ++i)
{
// ?
}
in this case, you know at the start of the loop iteration that i
is less than 5
.
The second is much, much more information than the first, no? Now, the programmer intent is (almost certainly) the same, but if you are looking for bugs, having confidence from reading a line of code is a good thing. And the second enforces that invariant, which means some bugs that would bite you in the first case just cannot happen (or don't cause memory corruption, say) in the second case.
You know more about the state of the program, from reading less code, with <
than with !=
. And on modern CPUs, they take the same amount of time as no difference.
If your i
was not manipulated in the loop body, and it was always increased by 1, and it started less than 5
, there would be no difference. But in order to know if it was manipulated, you'd have to confirm each of these facts.
Some of these facts are relatively easy, but you can get wrong. Checking the entire body of the loop is, however, a pain.
In C++ you can write an indexes
type such that:
for( const int i : indexes(0, 5) )
{
// ?
}
does the same thing as either of the two above for
loops, even down to the compiler optimizing it down to the same code. Here, however, you know that i
cannot be manipulated in the body of the loop, as it is declared const
, without the code corrupting memory.
The more information you can get out of a line of code without having to understand the context, the easier it is to track down what is going wrong. <
in the case of integer loops gives you more information about the state of the code at that line than !=
does.
It may happen that the variable i
is set to some large value and if you just use the !=
operator you will end up in an endless loop.
Iterators are an important case when you most often use the !=
notation:
for(auto it = vector.begin(); it != vector.end(); ++it) {
// do stuff
}
Granted: in practice I would write the same relying on a range-for
:
for(auto & item : vector) {
// do stuff
}
but the point remains: one normally compares iterators using ==
or !=
.