> So instead of trying to enumerate them all individually (which probably wasn't possible anyway), they identified the areas where they knew they could define firm semantics and allowed the stuff outside that boundary to be "undefined", so existing environments could continue to implement them compatibly.
These things didn't become undefined behavior. They became implementation defined behavior. The distinction is that for implementation defined behavior, a compiler has to make a decision consistently.
The big point of implementation defined behavior is 1s vs 2s complement. I believe shifting bits off the end of an unsigned int is also considered implementation defined.
For implementation defined behavior, the optimization of "assume it never happens" isn't allowed by the standard.
They did have implementation defined behavior, but a large part of undefined behavior was exactly that: never define anywhere and could have always been raised to implementation defined if they had thought to mention it.
I don't doubt what you're saying is true, I have heard similar things many many times over the years. The problem is that it's always stated somewhat vaguely, never with concrete examples, and it doesn't match my (perhaps naive) reading of any of the standards.
For example, I just checked C99[1]: it says in many places "If <X>, the behavior is undefined". It also says in even more places "<X> is implementation-defined" (although from my cursory inspection, most -- but not all -- of these seem to be about the behavior of library functions, not the compiler per se).
So it seems to me that the standards writers were actually very particular about the difference between implementation-defined behavior and undefined behavior.
I think bluGill might be referring to cases of undefined behavior which are undefined because the specification literally never mentions the behavior, as opposed to explicitly saying the behavior is undefined.
My canonical example of such a case is what happens if you call qsort where the comparison function is "int compare(const void*, const void*) { return 1; }".
Are those instances of undefined behavior relevant to what's being discussed here? The vast majority undefined behavior people argue/warn/complain about, including the original article, is behavior that is explicitly defined to be undefined (I say that with the caveat that I almost never use C++ and I've never read any C++ standard closely, so my perception is biased towards C; things might be different for C++).
What I mean to say is that the "problem" of undefined behavior does seem to be intentionally introduced by the authors of the standard, not an oversight.
These things didn't become undefined behavior. They became implementation defined behavior. The distinction is that for implementation defined behavior, a compiler has to make a decision consistently.
The big point of implementation defined behavior is 1s vs 2s complement. I believe shifting bits off the end of an unsigned int is also considered implementation defined.
For implementation defined behavior, the optimization of "assume it never happens" isn't allowed by the standard.