What is undefined behavior? What's the difference between implementation-defined, and undefined behavior? And why would we want things to have undefined behavior?
I think the confusion starts there.
I don't know of any system that implements volatile as atomic, so this is theoretical, but sure you could technically make your own compiler and define your volatile to be atomic, ok great.You could even go one step further and find every single instance of undefined behavior and make it do something predictable. Ok, congratulations you made a C++ compiler, one that is well behaved always.
But as far as the standard is concerned nothing has changed. I don't use your compiler, I use 6 other different ones, some of them are allowed to release flying monkeys from my hard drive, and they are still valid C++ compilers.
Me as a user cannot rely on code using undefined behavior to behave predictably on all compilers, as I may not use your "well defined" exclusively.
If it was "implementation-defined" on the other hand, I may still get different behavior on different compilers but in theory I should be able to read the compiler's manual and get consistent behavior in every compiler (as long as I appropriately make all of the #if pre-compiler directives right) and there will be no flying monkeys.
As a compiler vendor:implementation-defined = I must pick a behavior and do that consistently always.undefined behavior = I can do whatever I want, including having different results in the exact same circumstances.
As a user:implementation-defined = It's not consistent in every compiler, but if I stick to one compiler it will do what it says in the documentation consistently.undefined means = this may not be documented, and may not have consistent behavior even in the same compiler.
From: jim x <xmh970252187@gmail.com>
Sent: Saturday, October 10, 2026 3:49:24 AM
To: Tiago Freire <tmiguelf@hotmail.com>
Cc: std-discussion@lists.isocpp.org <std-discussion@lists.isocpp.org>; Jennifier Burnett <jenni@jburnett.me.uk>
Subject: Re: [std-discussion] Is this program undefined-behavior or implementation-defined behavior?
“volatile” is not atomic, I don’t know where you got that idea.
The idea is sourced from, since the standard states that the semantics of access through volatile glvalue are implementation-defined, an implementation can define the operation at `#1` as equivalent to `std::atomic_ref<int>(value).load()`, and the operation at `#2` as equivalent to `std::atomic_ref<int>(value).store()`, both are atomic operations defined in <atomic>, so under the specified semantics, why don't we consider them atomic?
On Fri, Oct 9, 2026 at 9:19 PM Tiago Freire <tmiguelf@hotmail.com> wrote:
1. “volatile” is not atomic, I don’t know where you got that idea.
2. Even with atomics there would be a data race, at least in the sense that running it multiple times will yield different results. Atomics only guarantee visibility of different types of side-effects (depending on the type of atomic operation) they do not the synchronize the execution across different threads. This is not exactly UB, what it does is actually very well defined, but the outcome of the operation is dependent on a factor that you neither control or the language specifies.
From: Std-Discussion <std-discussion-bounces@lists.isocpp.org> On Behalf Of jim x via Std-Discussion
Sent: Friday, October 9, 2026 08:28
To: Jennifier Burnett <jenni@jburnett.me.uk>
Cc: jim x <xmh970252187@gmail.com>; jim x via Std-Discussion <std-discussion@lists.isocpp.org>
Subject: Re: [std-discussion] Is this program undefined-behavior or implementation-defined behavior?
The issue is not whether an implementation can guarantee a specific behavior for an operation that has UB. The issue is why the operations at `#1` and `#2` are still considered non-atomic under the concrete semantics: An access to a
volatile intglvalue value is defined to have the exact semantics ofstd::atomic_ref<int>(value).load()for reads, andstd::atomic_ref<int>(value).store()for writes.
The premise you think the program is UB is that you think such two operations are non-atomic and therefore cause the program to contain a data race as per [intro.race] p17. My question is why such two operations are non-atomic under [intro.race] p17 when their semantics are specified as: An access to a
volatile intglvalue value is defined to have the exact semantics ofstd::atomic_ref<int>(value).load()for reads, andstd::atomic_ref<int>(value).store()for writes.
On Fri, Oct 9, 2026 at 2:16 PM Jennifier Burnett <jenni@jburnett.me.uk> wrote:
I'm not sure there's a conflict? Implementations are perfectly allowed to define undefined behaviour to have some specific behaviour. I'm allowed to create an implementation that has a defined behaviour for *all* data races, for example. The standard says that anything can happen when undefined behaviour occurs, and implementation-defined behaviour does fall under the category of "anything".
On 9 October 2026 06:11:15 BST, jim x via Std-Discussion <std-discussion@lists.isocpp.org> wrote:
Consider this example:
````cpp
#includeint main(){volatile int value = 0;std::jthread t1([&](){auto i = value; // #1});std::jthread t2([&](){value = 1; // #2});}````
People think this program should contain a data race because `#1` and `#2` are non-atomic operations, which satisfies the condition in [intro.races] p17
Two actions are potentially concurrent if
The execution of a program contains a data race if it contains two potentially concurrent conflicting actions, at least one of which is not atomic, and neither happens before the other, except for the special case for signal handlers described below. Any such data race results in undefined behavior ([ub:intro.races.data]).
However, this opinion is based on the premise that they consider the operations at `#1` and `#2` to be non-atomic operations in any way. However, [dcl.type.cv] p5 simultaneously states:
The semantics of an access through a volatile glvalue are implementation-defined.
How about if a hypothetical implementation specifies the semantics as follows:
An access to a
volatile intglvalue value is defined to have the exact semantics ofstd::atomic_ref<int>(value).load()for reads, andstd::atomic_ref<int>(value).store()for writes.
Under these specified semantics, `#1` and `#2` should be considered as atomic operations, which won't satisfy the condition in [intro.race] p17, therefore no data race. From this perspective, isn't the program implementation-defined behavior? That is, whether the program contains a data race depends on how the implementation specifies the semantics