Where does the standard define that a volatile variable can change undetected?
I've found two normative text which are about volatile:
Reading an object designated by a volatile glvalue ([basic.lval]), modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. Evaluation of an expression (or a subexpression) in general includes both value computations (including determining the identity of an object for glvalue evaluation and fetching a value previously assigned to an object for prvalue evaluation) and initiation of side effects. When a call to a library I/O function returns or an access through a volatile glvalue is evaluated the side effect is considered complete, even though some external actions implied by the call (such as the I/O itself) or by the volatile access may not have completed yet.
Is this paragraph about the undetected change? Could the side effect mean this?
Or there is dcl.type.cv/5:
The semantics of an access through a volatile glvalue are implementation-defined. If an attempt is made to access an object defined with a volatile-qualified type through the use of a non-volatile glvalue, the behavior is undefined.
Is this paragraph about my question? What does "The semantics of an access through a volatile glvalue are implementation-defined" mean exactly? Can you give an example of different "semantics of an access"?
And there is dcl.type.cv/6, which is about my question, but it is just a note:
[ Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. Furthermore, for some implementations, volatile might indicate that special hardware instructions are required to access the object. See [intro.execution] for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C. — end note ]
The key here is "changes in the state of the execution environment."
The execution environment is what's outside your program. That might include an OS, filesystem, screen, etcetera. It is generally unpredictable. You cannot assume that if you write a 0 to a file, that the file will not be overwritten by another process with a 1.
volatile variables are logically part of that execution environment. As far as C++ is concerned, the environment can enumerate them, read them and write them, just like files. And this can happen without your program knowing.
On the flip side, your implementation realizes the link between your program and its execution environment, so it does have some idea about what might happen. If it has some kind of private RAM disk implementation, then it might know that certain filenames are not externally visible in the OS filesystem. And it might know that
volatile int i lives in a CPU register, so that it can't be accessed via memory mappings. That's all allowed by the C++ standard. It just talks about the execution environment in general terms, the implementation must be more precise. That is what "implementation-defined semantics" means.