Date: Wed, 14 Oct 2026 09:20:07 +0200
You need to understand what "implementation-defined" means.
When the standard says something is "implementation-defined", it gives
the implementation a list of concrete, precise and well specified
options for the behaviour, and insists that the implementation pick one,
document it, and stick to it.
When the standards says something is "undefined behaviour", it says
nothing about the behaviour, and makes no requirements on the
implementation for either documentation or how it handles the construct.
These are clearly very different concepts.
Sometimes I see people who want the standards to say "this has
implementation-defined behaviour or undefined behaviour" because they
think it allows implementations to define what happens. This is
logically the same as simply saying "undefined behaviour" - it is like
"false && x" evaluating to false, regardless of x. Implementations are
already allowed to give any implementation they like for something that
is undefined behaviour.
The exact rules covering concurrent behaviour and data races are
complex, but we can simplify for your case and pretend the standards say
that there is a data race unless the accesses in the two threads use
atomic operations. "Atomic" operations are defined by the library (in
the <atomic> header), and data races are explicitly undefined behaviour.
If that were to be changed to "implementation-defined behaviour", then
the standard would have to say what possible behaviours the
implementation can choose from. What would be in that list? Clearly,
you want to include "volatile accesses can act like atomics" in some
way, but what about everything else? If the standard writes
"implementation-defined" here, it might conceivably suit your
hypothetical implementation where volatile accesses can easily be seen
as atomic, but it also means every other implementation has to define
exactly what happens in data race situations. The prime reason that
data races are UB is that hardware does not provide rules for what
happens - so implementations would have to make real atomic access
sequences for every memory access that they could not guarantee was free
from data races. Almost all data access would be surrounded by bus
locks, memory barrier instructions, load-link/store-conditional
sequences, etc. Or C++ would need a global thread lock, like the Python
GIL.
On 14/10/2026 04:41, jim x via Std-Discussion wrote:
> Doesn't such behavior depend on how the implementation defines its
> semantics? That is, *shouldn't it be considered implementation-defined,
> rather than unconditionally undefined?*
>
> On Tue, Oct 13, 2026 at 8:43 PM Andrey Semashev via Std-Discussion <std-
> discussion_at_[hidden] <mailto:std-discussion_at_[hidden]>> wrote:
>
> On 13 Oct 2026 15:18, Giuseppe D'Angelo via Std-Discussion wrote:
> > Il 13/10/26 12:24, Andrey Semashev via Std-Discussion ha scritto:
> >> You got it backwards. If the standard doesn't say volatile
> operations
> >> are atomic then they are not. Or put it another way, you cannot
> rely on
> >> them being atomic. Atomicity is not implied, the standard must
> >> explicitly require an operation to be atomic for an
> implementation to
> >> make an effort in making them atomic in the compiled program.
> >
> > Devil's advocate here: can an implementation say that reading a
> > `volatile int x` is defined as doing `atomic_ref<int>(x).load()`?
> >
> > (Same for the rest of operations.)
>
> It can, and that would be an example of an implementation-defined
> behavior.
>
> > If yes, would that make concurrent access to volatile ints not
> subject
> > to data races?
>
> On that particular implementation, yes. According to the standard or on
> other implementations that do not use atomic operations for
> volatiles, no.
>
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden] <mailto:Std-Discussion_at_[hidden]>
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
> <https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion>
>
>
When the standard says something is "implementation-defined", it gives
the implementation a list of concrete, precise and well specified
options for the behaviour, and insists that the implementation pick one,
document it, and stick to it.
When the standards says something is "undefined behaviour", it says
nothing about the behaviour, and makes no requirements on the
implementation for either documentation or how it handles the construct.
These are clearly very different concepts.
Sometimes I see people who want the standards to say "this has
implementation-defined behaviour or undefined behaviour" because they
think it allows implementations to define what happens. This is
logically the same as simply saying "undefined behaviour" - it is like
"false && x" evaluating to false, regardless of x. Implementations are
already allowed to give any implementation they like for something that
is undefined behaviour.
The exact rules covering concurrent behaviour and data races are
complex, but we can simplify for your case and pretend the standards say
that there is a data race unless the accesses in the two threads use
atomic operations. "Atomic" operations are defined by the library (in
the <atomic> header), and data races are explicitly undefined behaviour.
If that were to be changed to "implementation-defined behaviour", then
the standard would have to say what possible behaviours the
implementation can choose from. What would be in that list? Clearly,
you want to include "volatile accesses can act like atomics" in some
way, but what about everything else? If the standard writes
"implementation-defined" here, it might conceivably suit your
hypothetical implementation where volatile accesses can easily be seen
as atomic, but it also means every other implementation has to define
exactly what happens in data race situations. The prime reason that
data races are UB is that hardware does not provide rules for what
happens - so implementations would have to make real atomic access
sequences for every memory access that they could not guarantee was free
from data races. Almost all data access would be surrounded by bus
locks, memory barrier instructions, load-link/store-conditional
sequences, etc. Or C++ would need a global thread lock, like the Python
GIL.
On 14/10/2026 04:41, jim x via Std-Discussion wrote:
> Doesn't such behavior depend on how the implementation defines its
> semantics? That is, *shouldn't it be considered implementation-defined,
> rather than unconditionally undefined?*
>
> On Tue, Oct 13, 2026 at 8:43 PM Andrey Semashev via Std-Discussion <std-
> discussion_at_[hidden] <mailto:std-discussion_at_[hidden]>> wrote:
>
> On 13 Oct 2026 15:18, Giuseppe D'Angelo via Std-Discussion wrote:
> > Il 13/10/26 12:24, Andrey Semashev via Std-Discussion ha scritto:
> >> You got it backwards. If the standard doesn't say volatile
> operations
> >> are atomic then they are not. Or put it another way, you cannot
> rely on
> >> them being atomic. Atomicity is not implied, the standard must
> >> explicitly require an operation to be atomic for an
> implementation to
> >> make an effort in making them atomic in the compiled program.
> >
> > Devil's advocate here: can an implementation say that reading a
> > `volatile int x` is defined as doing `atomic_ref<int>(x).load()`?
> >
> > (Same for the rest of operations.)
>
> It can, and that would be an example of an implementation-defined
> behavior.
>
> > If yes, would that make concurrent access to volatile ints not
> subject
> > to data races?
>
> On that particular implementation, yes. According to the standard or on
> other implementations that do not use atomic operations for
> volatiles, no.
>
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden] <mailto:Std-Discussion_at_[hidden]>
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
> <https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion>
>
>
Received on 2026-10-14 07:20:16
