@Cubbi:
Yes, it produces different results on different compilers, as expected. GCC diagnoses the error (with -Wall). Other compilers I've tried don't diagnose it |
No, man. Don't try to escape with this. You know, ALL compilers give you the SAME output. Of course, GCC might gives you a WARNING (Not error, okay? Do you really think ANY compiler will let you get away with an error?), which might not be produced by other compilers (I've not tried personally, though). That's not "different results", okay? That's only the slightly different choices among compiler implementations on certain optional warnings.
but undefined behavior is not something compilers are required to diagnose. |
Interesting ... yes, you're right, the compiler might not be required to diagnose that. That is because it is not practically feasible to REQUIRE compilers to do that ALL THE TIME, not because that compilers don't want to check undefined behaviour. Okay? Everyone wants to get rid of undefined behaviour, and there's simply no way to make a machine to perform undefined behaviour. No matter what, even some undefined behaviour manages to escape the compiler checks, a run time error will be reported - e.g. divide by 0.
Okay, in this case, if
The expression cout << *p << " - " << *(p++) << endl; is an error in C++, it invokes undefined behavior |
, as you said, how is it possible for the program to finish without at least run time errors?
The compiler may evaluate the subexpressions in any order it chooses (barring a few limits imposed by sequence points/sequenced-before rules), taking into account pipelining, memory alignment, locality of reference, and any other low-level details about the target platform and the wider context that would produce fast-executing code. |
Generally speaking, you're not too wrong. But don't over-generalize the problem and then conclude with "Different compilers behave differently". That way, we're not going to have constructive discussions, because in the worst case, you can write your own compiler and say "look, it works". In the context (the << operator), I'm afraid you're way too wrong. Even if you want to build your worst case compiler, I'm afraid you can't. Let us assume that << operator is not evaluated from right to left, then you will never see a complete and correct string on your terminal.
Why is that?
<< takes parameters and return ostream object references. The ultimate purpose is to modify the cout ostream object such that the complete contents in the line of code can be shown on the screen. If
case 1:
cout << XXX(a new ostream object reference)
is not evaluated last, then you don't put everything into the cout ostream object. Therefore, you don't see the complete string.
case 2:
cout << XXX(a new ostream object reference)
is evaluated last, but some random order is followed - then we see a complete but wrongly ordered string.
Look, the idea is really simple: we are creating a bigger and bigger ostream object - by the time it reaches the cout object, a complete string has be constructed, in correct order. Then we see the correct and complete string on the terminal.