• Advertisement
Sign in to follow this  

less than opposed to not equal in for loops

This topic is 3035 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
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).

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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)?

Share this post


Link to post
Share on other sites
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().

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
If you consider data corruption from external sources, then what do you do about code that gets corrupted? ;)

Share this post


Link to post
Share on other sites
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 catching
for(int i = 0; i <= maxI; i += 2)
// ...
?

Share this post


Link to post
Share on other sites
Quote:
Original post by iMalc
Quote:
Original post by Sneftel
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 isn't an assertion. The point of the example (at least, as I used it) was to show how a particular coding idiom which valued generality could lead to hidden bugs, not how a specific code block could be rewritten to elucidate bugs in that particular block. The whole point was writing the loop without thinking about it, and the consequences thereof.

Share this post


Link to post
Share on other sites
Thanks everyone, a lot has been cleared up for me.

frob, How would I go about implementing a function into a for-loop now? That's completely new to me, but I can see using a while-loop to do that instead.

Share this post


Link to post
Share on other sites
One thing to note is that doing greater than/less than comparisons is (in general) more expensive than checking for equality. If this code is time dependent then you may want to do that and add an assert to make sure the value is always smaller when running a debug build.

Share this post


Link to post
Share on other sites
Quote:
Original post by rethan
One thing to note is that doing greater than/less than comparisons is (in general) more expensive than checking for equality.


I can't think of a single example where the less-than comparison might be more expensive than the check for equality. Would you care to show us one?

Share this post


Link to post
Share on other sites
If I am correct at this point then, a reason to use != would be because then your problem shows up a little quicker, and is easier to spot so you can go back and come up with an algorithm to be able to run the for loop as desired, but if you have to do that in the first place, you obviously didn't plan the loop very well.

I think I'll have to write a blog entry on all this, but keep the discussion going, a lot of what I'm reading here is interesting and every time I re-read the replies I think I learn a little bit more.

Share this post


Link to post
Share on other sites
Quote:
Original post by alvaro
Quote:
Original post by rethan
One thing to note is that doing greater than/less than comparisons is (in general) more expensive than checking for equality.


I can't think of a single example where the less-than comparison might be more expensive than the check for equality. Would you care to show us one?



Ah I did some research and you're right so I take that back. I thought the XOR instruction was faster then CMP but it's not (CMP doesn't save results which I'm guessing is where it gets its speed.), anyhoo I thought compilers would use this for != and == but that's not the case, since it looks like CMP is as fast or faster thank XOR in all cases (mem+reg, reg+imm, ...)

http://home.comcast.net/~fbui/intel_c.html#cmp
http://home.comcast.net/~fbui/intel_x.html#xor

Sorry!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement