Compilers don't get to just make code transformations willy-nilly, they have to follow the as-if rule, which basically states that the generated program must behave as if it executes the code as written in the input program. What makes the optimization you refer to admissible - even in old school C++03 - is that the compiler must be able to prove that the value of shared_var
does not change between the two references to local_copy_of_shared_var
. Typically that means that all the intervening code is visible to the compiler and that it contains no assignments to shared_var
.
This optimization is still legal in C++11 if shared_var
is a non-atomic type, since any concurrent modification of shared_var
in another thread would be a data race, and hence undefined behavior. Making shared_var
a C++11 atomic is a notice to the compiler that it can't prove that shared_var
doesn't change between the two references, since it might be changed by another thread, and that this particular optimization would not comply with the as-if rule.
TLDR: Compilers in general are forbidden to introduce spurious reads to atomics as they would introduce data races.