1. Abstract
This paper is the library part of P1152. The Core language parts of the deprecation were voted into C++20 at the Cologne meeting as [P1152R4]. LWG was unable to review library wording, this paper therefore carries forward the library parts of [P1152R3].
2. Edit History
2.1. r0 → r1
Address LWG feedback:

Change one "they" to "it".

Don’t use Mandates: clause, use Constraints: instead.

Clarify why we don’t have a feature test macro.
3. Wording
The proposed wording follows the library approach to deprecation: library deprecation presents the library without the deprecated feature, and only mentions said feature in Annex D.
No feature test macro is added, per SG10 guidance: developers cannot use a feature test macro to decide to do something else after this deprecation, they should instead fix the code even before the deprecation.
3.1. Tuples [tuple]
Modify as follows.
Header
synopsis [tuple.syn]:
< tuple >
namespace std { [...] // [ tuple.helper ], tuple helper classes template < class T > class tuple_size ; // not defined template < class T > class tuple_size < const T > ; template < class T > class tuple_size < volatile T > ; template < class T > class tuple_size < const volatile T > ; template < class ... Types > class tuple_size < tuple < Types ... >> ; template < size_t I , class T > class tuple_element ; // not defined template < size_t I , class T > class tuple_element < I , const T > ; template < size_t I , class T > class tuple_element < I , volatile T > ; template < size_t I , class T > class tuple_element < I , const volatile T > ; [...] } [...]
Tuple helper classes [tuple.helper]
template < class T > class tuple_size < const T > ; template < class T > class tuple_size < volatile T > ; template < class T > class tuple_size < const volatile T > ; Let
denote
TS of the cvunqualified type
tuple_size < T > . If the expression
T is wellformed when treated as an unevaluated operand, then
TS :: value each of the three templatesthe template shall satisfy therequirements with a base characteristic of
TransformationTrait
integral_constant < size_t , TS :: value > Otherwise,
theyit shall have no member.
value Access checking is performed as if in a context unrelated to
and
TS . Only the validity of the immediate context of the expression is considered. [ Note: The compilation of the expression can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitlydefined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being illformed. —end note ]
T In addition to being available via inclusion of the
header, the
< tuple > three templates aretemplate is available when any of the headers,
< array > , or
< ranges > are included.
< utility >
template < size_t I , class T > class tuple_element < I , const T > ; template < size_t I , class T > class tuple_element < I , volatile T > ; template < size_t I , class T > class tuple_element < I , const volatile T > ; Let
denote
TE of the cvunqualified type
tuple_element_t < I , T > . Then
T each of the three templatesthe template shall satisfy therequirements with a member typedef
TransformationTrait that names the
type followingtype:.
add_const_t < TE >
 for the first specialization,
,
add_const_t < TE >  for the second specialization,
, and
add_volatile_t < TE >  for the third specialization,
.
add_cv_t < TE > In addition to being available via inclusion of the
header, the
< tuple > three templates aretemplate is available when any of the headers,
< array > , or
< ranges > are included.
< utility >
3.2. Variants [variant]
Modify as follows.
synopsis [variant.syn]
< variant >
namespace std { // [ variant.variant ], class template variant template < class ... Types > class variant ; // [ variant.helper ], variant helper classes template < class T > struct variant_size ; // not defined template < class T > struct variant_size < const T > ; template < class T > struct variant_size < volatile T > ; template < class T > struct variant_size < const volatile T > ; template < class T > inline constexpr size_t variant_size_v = variant_size < T >:: value ; template < class ... Types > struct variant_size < variant < Types ... >> ; template < size_t I , class T > struct variant_alternative ; // not defined template < size_t I , class T > struct variant_alternative < I , const T > ; template < size_t I , class T > struct variant_alternative < I , volatile T > ; template < size_t I , class T > struct variant_alternative < I , const volatile T > ; [...] }
helper classes [variant.helper]
variant
template < class T > struct variant_size ; Remark: All specializations of
shall satisfy the
variant_size requirements with a base characteristic of
UnaryTypeTrait for some
integral_constant < size_t , N > .
N template < class T > class variant_size < const T > ; template < class T > class variant_size < volatile T > ; template < class T > class variant_size < const volatile T > ; Let
denote
VS of the cvunqualified type
variant_size < T > . Then
T each of the three templatesthe template shall satisfy therequirements with a base characteristic of
UnaryTypeTrait .
integral_constant < size_t , VS :: value >
template < class ... Types > struct variant_size < variant < Types ... >> : integral_constant < size_t , sizeof ...( Types ) > { };
template < size_t I , class T > class variant_alternative < I , const T > ; template < size_t I , class T > class variant_alternative < I , volatile T > ; template < size_t I , class T > class variant_alternative < I , const volatile T > ; Let
denote
VA of the cvunqualified type
variant_alternative < I , T > . Then
T each of the three templatesthe template shall meet therequirements with a member typedef
TransformationTrait that names the
type followingtype:.
add_const_t < VA :: type >
 for the first specialization,
,
add_const_t < VA :: type >  for the second specialization,
, and
add_volatile_t < VA :: type >  for the third specialization,
.
add_cv_t < VA :: type >
3.3. Atomic operations library [atomics]
Modify as follows.
Operations on atomic types [atomics.types.operations]
[ Note: Many operations are
qualified. The "volatile as device register" semantics have not changed in the standard. This qualification means that volatility is preserved when applying these operations to volatile objects. It does not mean that operations on nonvolatile objects become volatile. —end note ]
volatile [...]
bool is_lock_free () const volatile noexcept ; bool is_lock_free () const noexcept ; Returns:
true
if the object’s operations are lockfree,false
otherwise.[ Note: The return value of the
member function is consistent with the value of
is_lock_free for the same type. —end note ]
is_always_lock_free
void store ( T desired , memory_order order = memory_order :: seq_cst ) volatile noexcept ; void store ( T desired , memory_order order = memory_order :: seq_cst ) noexcept ; Requires: The
Constraints: For theargument shall not be
order ,
memory_order :: consume , nor
memory_order :: acquire .
memory_order :: acq_rel overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Atomically replaces the value pointed to by
with the value of
this . Memory is affected according to the value of
desired .
order Constraints: For the
T operator = ( T desired ) volatile noexcept ; T operator = ( T desired ) noexcept ; overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Equivalent to
.
store ( desired ) Returns:
.
desired
T load ( memory_order order = memory_order :: seq_cst ) const volatile noexcept ; T load ( memory_order order = memory_order :: seq_cst ) const noexcept ; Requires: The
Constraints: For theargument shall not be
order nor
memory_order :: release .
memory_order :: acq_rel overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Memory is affected according to the value of
.
order Returns: Atomically returns the value pointed to by
.
this Constraints: For the
operator T () const volatile noexcept ; operator T () const noexcept ; overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Equivalent to:
return load (); Constraints: For the
T exchange ( T desired , memory_order order = memory_order :: seq_cst ) volatile noexcept ; T exchange ( T desired , memory_order order = memory_order :: seq_cst ) noexcept ; overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Atomically replaces the value pointed to by
with
this . Memory is affected according to the value of
desired . These operations are atomic readmodifywrite operations.
order Returns: Atomically returns the value pointed to by
immediately before the effects.
this
bool compare_exchange_weak ( T & expected , T desired , memory_order success , memory_order failure ) volatile noexcept ; bool compare_exchange_weak ( T & expected , T desired , memory_order success , memory_order failure ) noexcept ; bool compare_exchange_strong ( T & expected , T desired , memory_order success , memory_order failure ) volatile noexcept ; bool compare_exchange_strong ( T & expected , T desired , memory_order success , memory_order failure ) noexcept ; bool compare_exchange_weak ( T & expected , T desired , memory_order order = memory_order :: seq_cst ) volatile noexcept ; bool compare_exchange_weak ( T & expected , T desired , memory_order order = memory_order :: seq_cst ) noexcept ; bool compare_exchange_strong ( T & expected , T desired , memory_order order = memory_order :: seq_cst ) volatile noexcept ; bool compare_exchange_strong ( T & expected , T desired , memory_order order = memory_order :: seq_cst ) noexcept ; Requires: The
Constraints: For theargument shall not be
failure nor
memory_order :: release .
memory_order :: acq_rel overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Retrieves the value in
. It then atomically compares the value representation of the value pointed to by
expected for equality with that previously retrieved from
this ,eand if true, replaces the value pointed to by
expected with that in
this . If and only if the comparison is true, memory is affected according to the value of
desired , and if the comparison is false, memory is affected according to the value of
success . When only one
failure argument is supplied, the value of
memory_order is
success , and the value of
order is
failure except that a value of
order shall be replaced by the value
memory_order :: acq_rel and a value of
memory_order :: acquire shall be replaced by the value
memory_order :: release . If and only if the comparison is false then, after the atomic operation, the value in
memory_order :: relaxed is replaced by the value pointed to by
expected during the atomic comparison. If the operation returns
this true
, these operations are atomic readmodifywrite operations on the memory pointed to by. Otherwise, these operations are atomic load operations on that memory.
this Returns: The result of the comparison.
[...]
Specializations for integers [atomics.types.int]
Constraints: For the
T fetch_ key ( T operand , memory_order order = memory_order :: seq_cst ) volatile noexcept ; T fetch_ key ( T operand , memory_order order = memory_order :: seq_cst ) noexcept ; overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Atomically replaces the value pointed to by
with the result of the computation applied to the value pointed to by
this and the given
this . Memory is affected according to the value of
operand . These operations are atomic readmodifywrite operations.
order Returns: Atomically, the value pointed to by
immediately before the effects.
this Remarks: For signed integer types, the result is as if the object value and parameters were converted to their corresponding unsigned types, the computation performed on those types, and the result converted back to the signed type. [ Note: There are no undefined results arising from the computation. —end note ]
Constraints: For the
T operator op = ( T operand ) volatile noexcept ; T operator op = ( T operand ) noexcept ; overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Equivalent to:
return fetch_ key ( operand ) op operand ; Specializations for floatingpoint types [atomics.types.float]
The following operations perform arithmetic addition and subtraction computations. The key, operator, and computation correspondence are identified in [atomic.arithmetic.computations].
Constraints: For the
T A :: fetch_ key ( T operand , memory_order order = memory_order_seq_cst ) volatile noexcept ; T A :: fetch_ key ( T operand , memory_order order = memory_order_seq_cst ) noexcept ; overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Atomically replaces the value pointed to by
with the result of the computation applied to the value pointed to by
this and the given
this . Memory is affected according to the value of
operand . These operations are atomic readmodifywrite operations.
order Returns: Atomically, the value pointed to by
immediately before the effects.
this Remarks: If the result is not a representable value for its type the result is unspecified, but the operations otherwise have no undefined behavior. Atomic arithmetic operations on
should conform to the
floating  point traits associated with the floatingpoint type. The floatingpoint environment for atomic arithmetic operations on
std :: numeric_limits < floating  point > may be different than the calling thread’s floatingpoint environment.
floating  point Constraints: For the
T operator op = ( T operand ) volatile noexcept ; T operator op = ( T operand ) noexcept ; overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Equivalent to:
return fetch_ key ( operand ) op operand ; Remarks: If the result is not a representable value for its type the result is unspecified, but the operations otherwise have no undefined behavior. Atomic arithmetic operations on
should conform to the
floating  point traits associated with the floatingpoint type. The floatingpoint environment for atomic arithmetic operations on
std :: numeric_limits < floating  point > may be different than the calling thread’s floatingpoint environment.
floating  point Partial specialization for pointers [atomics.types.pointer]
T * fetch_ key ( ptrdiff_t operand , memory_order order = memory_order :: seq_cst ) volatile noexcept ; T * fetch_ key ( ptrdiff_t operand , memory_order order = memory_order :: seq_cst ) noexcept ; Requires: T shall be an object type, otherwise the program is illformed. [ Note: Pointer arithmetic on
Constraints: For theor function pointers is illformed. —end note ]
void * overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Atomically replaces the value pointed to by
with the result of the computation applied to the value pointed to by
this and the given
this . Memory is affected according to the value of
operand . These operations are atomic readmodifywrite operations.
order Returns: Atomically, the value pointed to by
immediately before the effects.
this Remarks: The result may be an undefined address, but the operations otherwise have no undefined behavior.
Constraints: For the
T * operator op = ( ptrdiff_t operand ) volatile noexcept ; T * operator op = ( ptrdiff_t operand ) noexcept ; overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Equivalent to:
return fetch_ key ( operand ) op operand ; Member operators common to integers and pointers to objects [atomics.types.memop]
Constraints: For the
T operator ++ ( int ) volatile noexcept ; T operator ++ ( int ) noexcept ; overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Equivalent to:
return fetch_add ( 1 ); Constraints: For the
T operator  ( int ) volatile noexcept ; T operator  ( int ) noexcept ; overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Equivalent to:
return fetch_sub ( 1 ); Constraints: For the
T operator ++ () volatile noexcept ; T operator ++ () noexcept ; overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Equivalent to:
return fetch_add ( 1 ) + 1 ; Constraints: For the
T operator  () volatile noexcept ; T operator  () noexcept ; overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Equivalent to:
return fetch_sub ( 1 )  1 ; Nonmember functions [atomics.nonmembers]
A nonmember function template whose name matches the pattern
or the pattern
atomic_ f invokes the member function
atomic_ f _explicit , with the value of the first parameter as the object expression and the values of the remaining parameters (if any) as the arguments of the member function call, in order. An argument for a parameter of type
f is dereferenced when passed to the member function call. If no such member function exists, the program is illformed.
atomic < T >:: value_type * Constraints: For the
template < class T > void atomic_init ( volatile atomic < T >* object , typename atomic < T >:: value_type desired ) noexcept ; template < class T > void atomic_init ( atomic < T >* object , typename atomic < T >:: value_type desired ) noexcept ; overload of this function,
volatile is
atomic < T >:: is_always_lock_free true
.Effects: Nonatomically initializes
with value
* object . This function shall only be applied to objects that have been default constructed, and then only once. [ Note: These semantics ensure compatibility with C. —end note ] [ Note: Concurrent access from another thread, even via an atomic operation, constitutes a data race. —end note ]
desired [ Note: The nonmember functions enable programmers to write code that can be compiled as either C or C++, for example in a shared header file. —end note ]
3.4. Annex D
Add the following wording to Annex D:
3.4.1. Tuple [depr.tuple]
Header
synopsis [depr.tuple.syn]:
namespace std {
[...]
// [ tuple.helper ], tuple helper classes
template < class T > class tuple_size < volatile T > ;
template < class T > class tuple_size < const volatile T > ;
template < size_t I , class T > class tuple_element < I , volatile T > ;
template < size_t I , class T > class tuple_element < I , const volatile T > ;
[...]
}
Tuple helper classes [depr.tuple.helper]
template < class T > class tuple_size < volatile T > ;
template < class T > class tuple_size < const volatile T > ;
Let
denote
of the cvunqualified type
. If the
expression
is wellformed when treated as an unevaluated operand,
then each of the two templates shall satisfy the
requirements with a base characteristic of
integral_constant < size_t , TS :: value >
Otherwise, they shall have no member
.
Access checking is performed as if in a context unrelated to
and
.
Only the validity of the immediate context of the expression is considered.
In addition to being available via inclusion of the
header, the two
templates are available when any of the headers
,
, or
are included.
template < size_t I , class T > class tuple_element < I , volatile T > ;
template < size_t I , class T > class tuple_element < I , const volatile T > ;
Let
denote
of the cvunqualified type
. Then
each of the two templates shall satisfy the
requirements
with a member typedef
that names the following type:
 for the first specialization,
, andadd_volatile_t < TE >  for the second specialization,
.add_cv_t < TE >
In addition to being available via inclusion of the
header, the two
templates are available when any of the headers
,
, or
are included.
3.4.2. Variant [depr.variant]
synopsis [depr.variant.syn]
namespace std {
// [ variant.helper ], variant helper classes
template < class T > struct variant_size < volatile T > ;
template < class T > struct variant_size < const volatile T > ;
template < size_t I , class T > struct variant_alternative < I , volatile T > ;
template < size_t I , class T > struct variant_alternative < I , const volatile T > ;
}
helper classes [depr.variant.helper]
template < class T > class variant_size < volatile T > ; template < class T > class variant_size < const volatile T > ;
Let
denote
of the cvunqualified type
. Then each
of the two templates shall satisfy the
requirements with a
base characteristic of
.
template < size_t I , class T > class variant_alternative < I , volatile T > ;
template < size_t I , class T > class variant_alternative < I , const volatile T > ;
Let
denote
of the cvunqualified type
.
Then each of the two templates shall meet the
requirements with a member typedef
that names the following type:
 for the first specialization,
, andadd_volatile_t < VA :: type >  for the second specialization,
.add_cv_t < VA :: type >
3.4.3. Atomic operations library [depr.atomics]
If an atomic specialization has one of the following overloads, then that
overload is available when
is false
:
void store ( T desired , memory_order order = memory_order :: seq_cst ) volatile noexcept ;
T operator = ( T desired ) volatile noexcept ;
T load ( memory_order order = memory_order :: seq_cst ) const volatile noexcept ;
operator T () const volatile noexcept ;
T exchange ( T desired , memory_order order = memory_order :: seq_cst ) volatile noexcept ;
bool compare_exchange_weak ( T & expected , T desired , memory_order success , memory_order failure ) volatile noexcept ;
bool compare_exchange_strong ( T & expected , T desired , memory_order success , memory_order failure ) volatile noexcept ;
bool compare_exchange_weak ( T & expected , T desired , memory_order order = memory_order :: seq_cst ) volatile noexcept ;
bool compare_exchange_strong ( T & expected , T desired , memory_order order = memory_order :: seq_cst ) volatile noexcept ;
T fetch_ key ( T operand , memory_order order = memory_order :: seq_cst ) volatile noexcept ;
T operator op = ( T operand ) volatile noexcept ;
T * fetch_ key ( ptrdiff_t operand , memory_order order = memory_order :: seq_cst ) volatile noexcept ;
The following nonmember function is available when
is false
:
template < class T >
void atomic_init ( volatile atomic < T >* object , typename atomic < T >:: value_type desired ) noexcept ;