Probably the most common (or at least my favorite) example of DCE leading to a surprise, is when embedded programmers use so-called "delay loops" which can be eliminated by the compiler because it too is "empty" (the loop produces nothing that is used outside of the loop).
Essentially, C++ does not consider looping forever to be a side effect. You have to do something like writing to a volatile variable to ensure that the compiler does what you want.
"The implementation may assume that any thread will eventually do one of the following: — terminate, — make a call to a library I/O function, — access or modify a volatile object, or — perform a synchronization operation or an atomic operation. [ Note: This is intended to allow compiler transformations such as removal of empty loops, even when termination cannot be proven. — end note ]"