Wiggle Room
One of the primary goals of the C++ programming language, which it inherited from C, is to permit compilers to generate optimal code for their target platform. As a result, the C++ Standard doesn't spell out the exact details of things such as the size of the various integer types or the order of evaluation of arguments to functions. It leaves it to the compiler writer to decide what works best for the computer system that the compiler is targeting, and to fill in the details appropriately, within certain constraints.
For example, each of the integer types unsigned char, unsigned short, unsigned int, and unsigned long must be able to represent all of the values that can be represented by the type that precedes it in that list. In addition, each of the types must be able to represent all of the values in the minimal range specified for that type. As a result, this code snippet:
unsigned int i = 65535; cout < ++i < '\n';
can display the value 0 or the value 65536, depending on the actual range of values supported by an unsigned int [2]. The Standard requires that an unsigned int be able to store values that are greater than or equal to 0 and less than or equal to 65535. With a compiler that supports that exact range, incrementing the value of i makes it wrap around to 0. With a compiler that supports a larger range, incrementing the value of i simply sets it to the next value, 65536 [3].
To provide some guidance for users of features that have this sort of flexibility, the C++ Standard provides two categories for requirements that can be satisfied in more than one way: implementation-defined behavior and unspecified behavior. In both cases, the implementor chooses from a usually small set of alternatives. When something is designated as implementation-defined behavior, the implementor must document which choice was made. For unspecified behavior, no documentation is required. For example, the fundamental type char must have the same representation as either signed char or unsigned char. The choice is implementation-defined, so the documentation that comes with the compiler must tell you which one it is. On the other hand, the order of evaluation of arguments to a function is unspecified. The following code can write its two output lines in either order, and the implementor has no obligation to tell you what the order will be [4]:
int f() { cout < "In f\n"; return 3; } int g() { cout < "In g\n"; return 4; } int sum(int i, int j) { return i + j; } int main() { return sum(f(), g()); }
In both cases, if you want to write portable code, you should make sure that any code whose effect depends on implementation-defined or unspecified behavior is easily identifiable and well isolated, so that it can be changed easily if the need arises.
Well-Formed Programs, Ill-Formed Programs, And Diagnostic Messages
Some of the semantic rules in the C++ Standard explicitly say that if they are violated, "no diagnostic is required." Some say that violating the rule results in undefined behavior. Some say nothing about what behavior is required. The rest of the semantic rules are referred to as diagnosable semantic rules [5].
A well-formed program is a C++ program that does not violate any syntax rules, diagnosable semantic rules, or the One Definition Rule [6]. An ill-formed program is a program that is not a well-formed program. A diagnostic message is a message that is one of an implementation-defined subset of the implementation's output messages. If a program contains a violation of any diagnosable rule [7], the C++ Standard requires the implementation to issue at least one diagnostic message. If a program does not contain a violation of any of the rules (diagnosable or otherwise), and the program isn't too big for the compiler to handle [8], the C++ Standard requires the implementation to "accept and correctly execute" the program.
Read those definitions again, paying particular attention to what they don't say. They don't say that any code shouldn't compile, nor do they say anything about error messages or warnings. In fact, a compiler that always gives the message "compile successful," regardless of any violations of language rules, meets those requirements [9]. Of course, nobody would use such an uninformative compiler if they could avoid it, but that's just a practical detail.