std::atomic
allocates the T
object as a class member, so its internals can apply sufficient alignment.
It's not well-defined to point an atomic<T>*
at an existing T
object, although in many implementations it happens to work. That's the problem that std::atomic_ref<T>
is designed to solve; doing atomic accesses on a plain T
object that exists separately and which you might do some non-atomic accesses to in other phases of your program. Before C++20, the standard didn't provide a way to do that at all; that's why a programmer-visible requirement to align a plain T
object yourself is new in C++20 if you want to use atomic_ref
, like
alignas(std::atomic_ref<T>::required_alignment) T foo;
std::atomic_ref<T>::required_alignment
will be equal to alignof(std::atomic<T>)
, even if alignof(T)
is lower. At least in normal implementations.
Without std::atomic_ref
, you could of course use the compiler-specific extensions like GNU C __atomic
builtins on plain objects.
That's how std::atomic<T>
and std::atomic_ref<T>
are implemented in GCC / clang headers, rather than direct support in the compiler proper like with the C11 _Atomic
keyword. (Which has to do its own over-alignment of things it's applied to. There was GCC bug 65146 for a while where _Atomic
didn't force the alignment of int64_t
even on 32-bit targets where it only had 4-byte alignment such as x86; it worked outside structs because GCC chose to align int64_t
for performance, but was not actually atomic inside structs. G++'s std::atomic<>
had fixed that bug earlier, in the <atomic>
header, but C _Atomic
is internal to the compiler.)