Document: P3936R1 — Safer atomic_ref::address (FR-030-310)
Author: Corentin Jabot
Date: 2026-02-23
Audience: LWG (Library Working Group)
atomic_ref<T>::address() currently returns T*. That pointer lets you read and write the underlying object directly, bypassing the atomic discipline that atomic_ref is supposed to enforce — nothing in the type system stops you. The function was added by P2835R7 (merged November 2024) for legitimate lock-free use cases — hazard pointer registration, ABA-tag schemes — where you need the location of an object without a load. But the return type was always uncomfortable.
The blocker for fixing it was constexpr. uintptr_t is not universally mandated and breaks on architectures where pointers exceed the largest integer type. Casting void* back to T* in a constexpr context was not allowed. P2738R1 (already adopted for C++26) removed that restriction, which makes the fix viable: return void* instead of T*. Getting the typed pointer back still requires an explicit cast, making any non-atomic access visibly intentional. CV qualifiers from T are preserved via an exposition-only alias address-return-type = COPYCV(T, void)*, borrowed from [meta].
This resolves NB comment FR-030-310 from the C++26 ballot. Narrow scope, wording-only, design agreed in R0. Full paper: https://wg21.link/p3936r1
The thing worth saying explicitly:
void*does not prevent misuse.static_cast<T*>(ref.address())compiles fine and lets you write through it without going near an atomic operation. What changes is that the cast is now in the code. Anyone auditing the function sees it and knows something deliberate happened. That is a real improvement for code review, but it is not a safety guarantee.Also a small flag for anyone on draft C++26 implementations: P2835R7 shipped
address()returningT*. This paper changes the return type tovoid*. If your trunk compiler already implemented P2835R7, any code that assignsref.address()directly to aT*variable will fail to compile after this lands. Small audience, but non-zero.The paper frames it the same way:
Deliberate friction, not a hard stop. You are right that it is not prevention. But the NB comment was specifically about the API making accidental bypass the path of least resistance — returning
void*addresses that.wait,
atomic_refhas anaddress()member function? since whenP2835R7, merged late 2024. The use case is lock-free data structures where you need the raw address of the atomically-referenced object without performing a load — hazard pointers, generation counters, pointer tagging. You are not reading the value, just identifying the memory location.
address()gives you that handle. Until now it handed back aT*which invited accidental non-atomic use.i respect lock-free programmers the same way i respect free solo climbers. deeply impressive, completely insane, not for me
Some of us do not get to choose. When you are sharing a memory region between a main processor and a DSP with no OS between them,
atomic_refover a fixed-layout struct in shared RAM is exactly what you want. No mutexes in interrupt context.The
COPYCValias being duplicated from[meta]into[atomics.general]bothers me more than it should. The paper literally saysand then does not do it, citing out-of-scope. Reasonable call, but this is how the standard ends up with the same boilerplate macro in five different sections.
COPYCVis exposition-only so it has no normative user-visible presence — it never appears in the interface, only in the spec infrastructure. Factorizing it is a housekeeping item for LWG editorial, separate from this NB resolution. Mixing the two would block the fix for no user-visible gain. The punt is correct.From the implementation side: trivially correct. The
uintptr_tNB comment was dead on arrival — we have targets where pointer width exceeds the largest integer type, andconstexprcompatibility was already broken for it.void*with P2738R1 unblocking the cast is the obvious answer. Nothing to implement beyond changing a return type.the correct fix is to not use
atomic_refand sleep at nightcommittee gonna committee. one extra cast to prevent an accident that probably has never happened to anyone
godbolt.org — because you need to see the assembly.