r/wg21
P3936R1 - Safer atomic_ref::address (FR-030-310) Standards
Posted by u/hazard_ptr_adjacent_42 · 5 hr. ago

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

▲ 24 points (91% upvoted) · 11 comments
sorted by: best
u/lock_free_reluctant_99 19 points 4 hr. ago

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() returning T*. This paper changes the return type to void*. If your trunk compiler already implemented P2835R7, any code that assigns ref.address() directly to a T* variable will fail to compile after this lands. Small audience, but non-zero.

u/atomic_ref_was_a_mistake 11 points 3 hr. ago

The paper frames it the same way:

returning T* would make it too easy for callers to potentially circumvent the atomic operations on the referred-to object

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.

u/build_system_victim_2026 41 points 4 hr. ago

wait, atomic_ref has an address() member function? since when

u/hazard_ptr_adjacent_42 27 points 3 hr. ago

P2835R7, 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 a T* which invited accidental non-atomic use.

u/just_use_a_mutex_bro_cpp 94 points 2 hr. ago

i respect lock-free programmers the same way i respect free solo climbers. deeply impressive, completely insane, not for me

u/embedded_for_20_years 18 points 1 hr. ago

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_ref over a fixed-layout struct in shared RAM is exactly what you want. No mutexes in interrupt context.

u/copycv_is_not_a_person 15 points 3 hr. ago

The COPYCV alias being duplicated from [meta] into [atomics.general] bothers me more than it should. The paper literally says

LWG may wish to factorize the COPYCV definition introduced here

and 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.

u/wg21_wording_archaeologist 9 points 2 hr. ago

COPYCV is 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.

u/gcc_atomics_implementer_irl 33 points 2 hr. ago

From the implementation side: trivially correct. The uintptr_t NB comment was dead on arrival — we have targets where pointer width exceeds the largest integer type, and constexpr compatibility was already broken for it. void* with P2738R1 unblocking the cast is the obvious answer. Nothing to implement beyond changing a return type.

u/senior_mutex_advocate_2019 112 points 1 hr. ago

the correct fix is to not use atomic_ref and sleep at night

u/undefined_behavior_enjoyer_420 6 points 47 min. ago

committee gonna committee. one extra cast to prevent an accident that probably has never happened to anyone