less than opposed to not equal in for loops

Started by
14 comments, last by rethan 14 years, 5 months ago
Hi, I had a question about how a for-loop should be set up. In C++ Primer, the author seems to use != instead of < in all the for-loops, but my question is, why? What if you set up a for loop as such: for (int i = 0; i != 11; i += 2) then you've got a problem, because i will never equal 11. I know that specific example is very easy to see the problem, but if you had something a little bit more complex, I assume it would be fairly hard to find the problem to fix it. Therefore, my question is: Why do you suppose != is used in C++ Primer, instead of <? I am thinking I might be missing something important that will change my theory, but it makes logical sense that < should be used instead. Thanks, dbproguy
--Dbproguy - My Blog - Tips, Opinions and Reviews about C++, Video Games, and Life
Advertisement
It depends on the context, but one reason != is sometimes used instead of < is that in the context of iterating over the elements in a container, != is more general. As an example, a generic function that iterates over the elements in a container using != as the conditional will work with (e.g.) both vector and map, whereas if < were used, the function would not work with map. Another way to look at it is that != will work with more types of iterators than will <.

In your example above, clearly != would not be the right choice (again, which to use depends on the context).
A number of types which you can use as the counter in a for loop (in particular std::list::iterator), have no concept of ordering, and may not even implement the < operator. Not-equal is a concept which can be applied much more generally.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Quote:Original post by Dbproguy

What if you set up a for loop as such:
for (int i = 0; i != 11; i += 2)
then you've got a problem, because i will never equal 11.

You've got a problem either way. If you're iterating over only the even numbers in a range, and that range may be of a size which is an odd number, then you had better think hard about what should be done in that situation. Using < instead of != would certainly hide the problem, but it wouldn't necessarily fix it. In general, the kind of bugs that you WANT to have are the ones that result in obvious problems. It's the ones that are less visible that are the real time sinks. From that perspective, using != instead of < is a good thing to do, because it doesn't hide the sort of problem you posted.
So it's more of something to do when using iterators? In any case NOT using an iterator it's probably best to use the '<' operator though right? At least for the simple things

What would be the solution to iterating through the even numbered indexes of a container with an odd number? Would you just have to check and see if it has an odd number first then if it does, iterate to (x.end()-1)?
--Dbproguy - My Blog - Tips, Opinions and Reviews about C++, Video Games, and Life
because he prefers it. I really don't think there should be a reason for it.
taytay
Quote:Original post by Dbproguy
What would be the solution to iterating through the even numbered indexes of a container with an odd number? Would you just have to check and see if it has an odd number first then if it does, iterate to (x.end()-1)?
It seems you missed the point.

Which one you use depends entirely on your algorithm.

If your algorithm must run less than x, use < x.
If your algorithm must run to the end of the iterator, use != foo.end().
If your algorithm must run as long as something is true, use x == true.
If you algorithm must run for {whatever}, use {the thing that does it}.


So if your algorithm has a requirement that the iterator have an even number of elements, it is your job to ensure this is the case. If your algorithm needs different logic to handle even and odd element counts, it is your job to ensure that it does.

If you need to advance an iterator more than one space, you can use std::advance() to move it. It is still your responsibility to ensure that you do not advance past the end of the container. Just note that if you iterate past the end of the iterator, your results are undefined. The standards committee intentionally left out requirements about what to do when you iterate past the end --- there are potential performance issues and they do not like forcing extra costs when it can be avoided.



The answer to your particular problem seems obvious to me:

Rather than incrementing directly inside the loop, consider writing a small function/functor/predicate/whatever that does this for you. This can advance it once, and if the result != iter.end(), advance it a second time. You could get fancy and have one that advances n times. Basically implement std::advance() with an end-of-iterator safety check.





For iterators, know that ordering is undefined. For any iterator, .begin() is not always before .end(). Reverse iterators go from back to front. Several standard containers operate in blocks so the data can be allocated in just about any configuration. I have seen several different circular buffer containers with an iterator that loops in the middle. In each case, ordering may not be what you expect.

Many beginners think of iterators in the format {front,...,end}. They need to revise their thinking. There are iterators that travel in reverse {end,...,front}, circular {...,end,empty,front,...}, block allocations {...,begin,...,end,...}, and just about any other configuration imaginable.

Because iterator ordering is undefined, you should always compare directly against .begin() and .end().
Quote:Original post by Sneftel
Quote:Original post by Dbproguy

What if you set up a for loop as such:
for (int i = 0; i != 11; i += 2)
then you've got a problem, because i will never equal 11.

You've got a problem either way. If you're iterating over only the even numbers in a range, and that range may be of a size which is an odd number, then you had better think hard about what should be done in that situation. Using < instead of != would certainly hide the problem, but it wouldn't necessarily fix it. In general, the kind of bugs that you WANT to have are the ones that result in obvious problems. It's the ones that are less visible that are the real time sinks. From that perspective, using != instead of < is a good thing to do, because it doesn't hide the sort of problem you posted.
Double edged sword.
You want any logic errors in your for-loop iteration or ending conditions to be obvious in a debug build, but on the other hand you may not want a runaway loop to happen for a customer, thus it may be safer to use less-than in a release build where you'd rather hide any problem.

It depends on the application too. Your for loop with != logic might be bug-free, but what's the risk of a runaway loop on say a spacecraft just because one bit of a variable got corrupted? Or a little closer to home... the memory in your electric fence controller got corrupted by a voltage spike? In some cases, correctness isn't necessarily the whole story. Things get interesting when it's more than just external data that you can't trust.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
If you consider data corruption from external sources, then what do you do about code that gets corrupted? ;)
If you want to hide it from the customer, but see it in development, why not:
assert(maxI % 2 == 0); // or your choice of debug-build-only error catchingfor(int i = 0; i <= maxI; i += 2)    // ...
?

This topic is closed to new replies.

Advertisement