C++98/C++03 give no real consideration to multithreaded code, which means that optimizers were free to work on the basis of preserving only single-threaded correctness (and they did!). The only tools you have at your disposal are inline assembly, external function calls acting as natural barriers, and (misusing) volatile. (Or maybe you have compiler-specific builtins, e.g., the GCC __sync_ builtins).
In terms of changes to semantics of pre-C++11 code, the biggest change is that the compiler is no longer able to introduce stores to memory locations that would not have been stored normally. This prohibits the optimization that would change this loop:
for (int i = 0; i < N; i++)
*res += A[i];
into this:
int temp = *res;
for (int i = 0; i < N; i++)
temp += A[i];
*res = temp;
(since res would not have been written if N were 0 or less).
Beyond that, the main effect is that it is now possible to write correct multi-threaded, lock-free code without having to rely on the scattershot nature of volatile. Volatile does not mean what many people take it to mean. The closest approximation to its actual semantics in practice is that any load or store instruction generated from a volatile must not be reordered (with respect only to other such load/store instructions), and it must not be removed (i.e., no dead-store elimination or hoisting outside a loop). In particular, volatile does not prohibit load/store tearing (e.g., reading a 64-bit location via two separate 32-bit loads); it does not prohibit other accesses from being widened to include the volatile memory address.
In terms of changes to semantics of pre-C++11 code, the biggest change is that the compiler is no longer able to introduce stores to memory locations that would not have been stored normally. This prohibits the optimization that would change this loop:
into this: (since res would not have been written if N were 0 or less).Beyond that, the main effect is that it is now possible to write correct multi-threaded, lock-free code without having to rely on the scattershot nature of volatile. Volatile does not mean what many people take it to mean. The closest approximation to its actual semantics in practice is that any load or store instruction generated from a volatile must not be reordered (with respect only to other such load/store instructions), and it must not be removed (i.e., no dead-store elimination or hoisting outside a loop). In particular, volatile does not prohibit load/store tearing (e.g., reading a 64-bit location via two separate 32-bit loads); it does not prohibit other accesses from being widened to include the volatile memory address.