Annex D (normative) Compatibility features [depr]

D.1 General [depr.general]

This Annex describes features of the C++ Standard that are specified for compatibility with existing implementations.
These are deprecated features, where deprecated is defined as: Normative for the current revision of C++, but having been identified as a candidate for removal from future revisions.
An implementation may declare library names and entities described in this Clause with the deprecated attribute.

D.2 Implicit capture of *this by reference [depr.capture.this]

For compatibility with prior revisions of C++, a lambda-expression with capture-default = ([expr.prim.lambda.capture]) may implicitly capture *this by reference.
[Example 1: struct X { int x; void foo(int n) { auto f = [=]() { x = n; }; // deprecated: x means this->x, not a copy thereof auto g = [=, this]() { x = n; }; // recommended replacement } }; — end example]

D.3 Array comparisons [depr.array.comp]

Equality and relational comparisons ([expr.eq], [expr.rel]) between two operands of array type are deprecated.
[Note 1: 
Three-way comparisons ([expr.spaceship]) between such operands are ill-formed.
— end note]
[Example 1: int arr1[5]; int arr2[5]; bool same = arr1 == arr2; // deprecated, same as &arr1[0] == &arr2[0], // does not compare array contents auto cmp = arr1 <=> arr2; // error — end example]

D.4 Deprecated volatile types [depr.volatile.type]

Postfix ++ and -- expressions ([expr.post.incr]) and prefix ++ and -- expressions ([expr.pre.incr]) of volatile-qualified arithmetic and pointer types are deprecated.
[Example 1: volatile int velociraptor; ++velociraptor; // deprecated — end example]
Certain assignments where the left operand is a volatile-qualified non-class type are deprecated; see [expr.ass].
[Example 2: int neck, tail; volatile int brachiosaur; brachiosaur = neck; // OK tail = brachiosaur; // OK tail = brachiosaur = neck; // deprecated brachiosaur += neck; // OK — end example]
A function type ([dcl.fct]) with a parameter with volatile-qualified type or with a volatile-qualified return type is deprecated.
[Example 3: volatile struct amber jurassic(); // deprecated void trex(volatile short left_arm, volatile short right_arm); // deprecated void fly(volatile struct pterosaur* pteranodon); // OK — end example]
A structured binding ([dcl.struct.bind]) of a volatile-qualified type is deprecated.
[Example 4: struct linhenykus { short forelimb; }; void park(linhenykus alvarezsauroid) { volatile auto [what_is_this] = alvarezsauroid; // deprecated // ... } — end example]

D.5 Redeclaration of static constexpr data members [depr.static.constexpr]

For compatibility with prior revisions of C++, a constexpr static data member may be redundantly redeclared outside the class with no initializer ([basic.def], [class.static.data]).
This usage is deprecated.
[Example 1: struct A { static constexpr int n = 5; // definition (declaration in C++ 2014) }; constexpr int A::n; // redundant declaration (definition in C++ 2014) — end example]

D.6 Non-local use of TU-local entities [depr.local]

A declaration of a non-TU-local entity that is an exposure ([basic.link]) is deprecated.
[Note 1: 
Such a declaration in an importable module unit is ill-formed.
— end note]
[Example 1: namespace { struct A { void f() {} }; } A h(); // deprecated: not internal linkage inline void g() {A().f();} // deprecated: inline and not internal linkage — end example]

D.7 Implicit declaration of copy functions [depr.impldec]

The implicit definition of a copy constructor as defaulted is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.
The implicit definition of a copy assignment operator as defaulted is deprecated if the class has a user-declared copy constructor or a user-declared destructor.
It is possible that future versions of C++ will specify that these implicit definitions are deleted ([dcl.fct.def.delete]).

D.8 Literal operator function declarations using an identifier [depr.lit]

A literal-operator-id ([over.literal]) of the form operator string-literal identifier is deprecated.

D.9 template keyword before qualified names [depr.template.template]

The use of the keyword template before the qualified name of a class or alias template without a template argument list is deprecated ([temp.names]).

D.10 has_denorm members in numeric_limits [depr.numeric.limits.has.denorm]

The following type is defined in addition to those specified in <limits>: namespace std { enum float_denorm_style { denorm_indeterminate = -1, denorm_absent = 0, denorm_present = 1 }; }
The following members are defined in addition to those specified in [numeric.limits.general]: static constexpr float_denorm_style has_denorm = denorm_absent; static constexpr bool has_denorm_loss = false;
The values of has_denorm and has_denorm_loss of specializations of numeric_limits are unspecified.
The following members of the specialization numeric_limits<bool> are defined in addition to those specified in [numeric.special]: static constexpr float_denorm_style has_denorm = denorm_absent; static constexpr bool has_denorm_loss = false;

D.11 Deprecated C macros [depr.c.macros]

The header <stdalign.h> has the following macro: #define __alignas_is_defined 1 #define __alignof_is_defined 1
The header <stdbool.h> has the following macro: #define __bool_true_false_are_defined 1

D.12 Relational operators [depr.relops]

The header <utility> has the following additions: namespace std::rel_ops { template<class T> bool operator!=(const T&, const T&); template<class T> bool operator> (const T&, const T&); template<class T> bool operator<=(const T&, const T&); template<class T> bool operator>=(const T&, const T&); }
To avoid redundant definitions of operator!= out of operator== and operators >, <=, and >= out of operator<, the library provides the following:
template<class T> bool operator!=(const T& x, const T& y);
Preconditions: T meets the Cpp17EqualityComparable requirements (Table 28).
Returns: !(x == y).
template<class T> bool operator>(const T& x, const T& y);
Preconditions: T meets the Cpp17LessThanComparable requirements (Table 29).
Returns: y < x.
template<class T> bool operator<=(const T& x, const T& y);
Preconditions: T meets the Cpp17LessThanComparable requirements (Table 29).
Returns: !(y < x).
template<class T> bool operator>=(const T& x, const T& y);
Preconditions: T meets the Cpp17LessThanComparable requirements (Table 29).
Returns: !(x < y).

D.13 Deprecated error numbers [depr.cerrno]

The following macros are defined in addition to those specified in [cerrno.syn]:
#define ENODATA see below #define ENOSR see below #define ENOSTR see below #define ETIME see below
The meaning of these macros is defined by the POSIX standard.
The following enum errc enumerators are defined in addition to those specified in [system.error.syn]:
no_message_available, // ENODATA no_stream_resources, // ENOSR not_a_stream, // ENOSTR stream_timeout, // ETIME
The value of each enum errc enumerator above is the same as the value of the <cerrno> macro shown in the above synopsis.

D.14 Deprecated type traits [depr.meta.types]

The header <type_traits> has the following addition: namespace std { template<class T> struct is_pod; template<class T> constexpr bool is_pod_v = is_pod<T>::value; template<size_t Len, size_t Align = default-alignment> // see below struct aligned_storage; template<size_t Len, size_t Align = default-alignment> // see below using aligned_storage_t = typename aligned_storage<Len, Align>::type; template<size_t Len, class... Types> struct aligned_union; template<size_t Len, class... Types> using aligned_union_t = typename aligned_union<Len, Types...>::type; }
The behavior of a program that adds specializations for any of the templates defined in this subclause is undefined, unless explicitly permitted by the specification of the corresponding template.
A POD class is a class that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD class (or array thereof).
A POD type is a scalar type, a POD class, an array of such a type, or a cv-qualified version of one of these types.
template<class T> struct is_pod;
Preconditions: remove_all_extents_t<T> shall be a complete type or cv void.
Remarks: is_pod<T> is a Cpp17UnaryTypeTrait ([meta.rqmts]) with a base characteristic of true_type if T is a POD type, and false_type otherwise.
[Note 1: 
It is unspecified whether a closure type ([expr.prim.lambda.closure]) is a POD type.
— end note]
template<size_t Len, size_t Align = default-alignment> struct aligned_storage;
The value of default-alignment is the most stringent alignment requirement for any object type whose size is no greater than Len ([basic.types]).
Mandates: Len is not zero.
Align is equal to alignof(T) for some type T or to default-alignment.
The member typedef type denotes a trivial standard-layout type suitable for use as uninitialized storage for any object whose size is at most Len and whose alignment is a divisor of Align.
[Note 2: 
Uses of aligned_storage<Len, Align>​::​type can be replaced by an array std​::​byte[Len] declared with alignas(Align).
— end note]
[Note 3: 
A typical implementation would define aligned_storage as: template<size_t Len, size_t Alignment> struct aligned_storage { typedef struct { alignas(Alignment) unsigned char __data[Len]; } type; };
— end note]
template<size_t Len, class... Types> struct aligned_union;
Mandates: At least one type is provided.
Each type in the template parameter pack Types is a complete object type.
The member typedef type denotes a trivial standard-layout type suitable for use as uninitialized storage for any object whose type is listed in Types; its size shall be at least Len.
The static member alignment_value is an integral constant of type size_t whose value is the strictest alignment of all types listed in Types.

D.15 Tuple [depr.tuple]

The header <tuple> has the following additions: namespace std { template<class T> struct tuple_size<volatile T>; template<class T> struct tuple_size<const volatile T>; template<size_t I, class T> struct tuple_element<I, volatile T>; template<size_t I, class T> struct tuple_element<I, const volatile T>; }
template<class T> struct tuple_size<volatile T>; template<class T> struct tuple_size<const volatile T>;
Let TS denote tuple_size<T> of the cv-unqualified type T.
If the expression TS​::​value is well-formed when treated as an unevaluated operand, then specializations of each of the two templates meet the Cpp17TransformationTrait requirements with a base characteristic of integral_constant<size_t, TS​::​value>.
Otherwise, they have no member value.
Access checking is performed as if in a context unrelated to TS and T.
Only the validity of the immediate context of the expression is considered.
In addition to being available via inclusion of the <tuple> header, the two templates are available when any of the headers <array>, <ranges>, or <utility> are included.
template<size_t I, class T> struct tuple_element<I, volatile T>; template<size_t I, class T> struct tuple_element<I, const volatile T>;
Let TE denote tuple_element_t<I, T> of the cv-unqualified type T.
Then specializations of each of the two templates meet the Cpp17TransformationTrait requirements with a member typedef type that names the following type:
  • for the first specialization, add_volatile_t<TE>, and
  • for the second specialization, add_cv_t<TE>.
In addition to being available via inclusion of the <tuple> header, the two templates are available when any of the headers <array>, <ranges>, or <utility> are included.

D.16 Variant [depr.variant]

The header <variant> has the following additions: namespace std { 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>; }
template<class T> struct variant_size<volatile T>; template<class T> struct variant_size<const volatile T>;
Let VS denote variant_size<T> of the cv-unqualified type T.
Then specializations of each of the two templates meet the Cpp17UnaryTypeTrait requirements with a base characteristic of integral_constant<size_t, VS​::​value>.
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>;
Let VA denote variant_alternative<I, T> of the cv-unqualified type T.
Then specializations of each of the two templates meet the Cpp17TransformationTrait requirements with a member typedef type that names the following type:
  • for the first specialization, add_volatile_t<VA​::​type>, and
  • for the second specialization, add_cv_t<VA​::​type>.

D.17 Deprecated iterator class template [depr.iterator]

The header <iterator> has the following addition: namespace std { template<class Category, class T, class Distance = ptrdiff_t, class Pointer = T*, class Reference = T&> struct iterator { using iterator_category = Category; using value_type = T; using difference_type = Distance; using pointer = Pointer; using reference = Reference; }; }
The iterator template may be used as a base class to ease the definition of required types for new iterators.
[Note 1: 
If the new iterator type is a class template, then these aliases will not be visible from within the iterator class's template definition, but only to callers of that class.
— end note]
[Example 1: 
If a C++ program wants to define a bidirectional iterator for some data structure containing double and such that it works on a large memory model of the implementation, it can do so with: class MyIterator : public iterator<bidirectional_iterator_tag, double, long, T*, T&> { // code implementing ++, etc. };
— end example]

D.18 Deprecated move_iterator access [depr.move.iter.elem]

The following member is declared in addition to those members specified in [move.iter.elem]: namespace std { template<class Iterator> class move_iterator { public: constexpr pointer operator->() const; }; }
constexpr pointer operator->() const;
Returns: current.

D.19 Deprecated formatting [depr.format]

D.19.1 Header <format> synopsis [depr.format.syn]

The header <format>format.syn has the following additions: namespace std { template<class Visitor, class Context> decltype(auto) visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg); }

D.19.2 Formatting arguments [depr.format.arg]

template<class Visitor, class Context> decltype(auto) visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg);
Effects: Equivalent to: return visit(std​::​forward<Visitor>(vis), arg.value);

D.20 Deprecated locale category facets [depr.locale.category]

The ctype locale category includes the following facets as if they were specified in table Table 107 of [locale.category].
codecvt<char16_t, char, mbstate_t> codecvt<char32_t, char, mbstate_t> codecvt<char16_t, char8_t, mbstate_t> codecvt<char32_t, char8_t, mbstate_t>
The ctype locale category includes the following facets as if they were specified in table Table 108 of [locale.category].
codecvt_byname<char16_t, char, mbstate_t> codecvt_byname<char32_t, char, mbstate_t> codecvt_byname<char16_t, char8_t, mbstate_t> codecvt_byname<char32_t, char8_t, mbstate_t>
The following class template specializations are required in addition to those specified in [locale.codecvt].
The specializations codecvt<char16_t, char, mbstate_t> and codecvt<char16_t, char8_t, mbstate_t> convert between the UTF-16 and UTF-8 encoding forms, and the specializations codecvt<char32_t, char, mbstate_t> and codecvt<char32_t, char8_t, mbstate_t> convert between the UTF-32 and UTF-8 encoding forms.

D.21 Deprecated filesystem path factory functions [depr.fs.path.factory]

The header <filesystem> has the following additions:
template<class Source> path u8path(const Source& source); template<class InputIterator> path u8path(InputIterator first, InputIterator last);
Mandates: The value type of Source and InputIterator is char or char8_t.
Preconditions: The source and [first, last) sequences are UTF-8 encoded.
Source meets the requirements specified in [fs.path.req].
Returns:
  • If path​::​value_type is char and the current native narrow encoding ([fs.path.type.cvt]) is UTF-8, return path(source) or path(first, last); otherwise,
  • if path​::​value_type is wchar_t and the native wide encoding is UTF-16, or if path​::​value_type is char16_t or char32_t, convert source or [first, last) to a temporary, tmp, of type path​::​string_type and return path(tmp); otherwise,
  • convert source or [first, last) to a temporary, tmp, of type u32string and return path(tmp).
Remarks: Argument format conversion ([fs.path.fmt.cvt]) applies to the arguments for these functions.
How Unicode encoding conversions are performed is unspecified.
[Example 1: 
A string is to be read from a database that is encoded in UTF-8, and used to create a directory using the native encoding for filenames: namespace fs = std::filesystem; std::string utf8_string = read_utf8_data(); fs::create_directory(fs::u8path(utf8_string));
For POSIX-based operating systems with the native narrow encoding set to UTF-8, no encoding or type conversion occurs.
For POSIX-based operating systems with the native narrow encoding not set to UTF-8, a conversion to UTF-32 occurs, followed by a conversion to the current native narrow encoding.
Some Unicode characters may have no native character set representation.
For Windows-based operating systems a conversion from UTF-8 to UTF-16 occurs.
— end example]
[Note 1: 
The example above is representative of a historical use of filesystem​::​u8path.
To indicate a UTF-8 encoding, passing a std​::​u8string to path's constructor is preferred as it is consistent with path's handling of other encodings.
— end note]

D.22 Deprecated atomic operations [depr.atomics]

D.22.1 General [depr.atomics.general]

The header <atomic> has the following additions.
namespace std { template<class T> void atomic_init(volatile atomic<T>*, typename atomic<T>::value_type) noexcept; template<class T> void atomic_init(atomic<T>*, typename atomic<T>::value_type) noexcept; #define ATOMIC_VAR_INIT(value) see below }

D.22.2 Volatile access [depr.atomics.volatile]

If an atomic ([atomics.types.generic]) specialization has one of the following overloads, then that overload participates in overload resolution even if atomic<T>​::​is_always_lock_free 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;

D.22.3 Non-member functions [depr.atomics.nonmembers]

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;
Effects: Equivalent to: atomic_store_explicit(object, desired, memory_order​::​relaxed);

D.22.4 Operations on atomic types [depr.atomics.types.operations]

#define ATOMIC_VAR_INIT(value) see below
The macro expands to a token sequence suitable for constant initialization of an atomic variable of static storage duration of a type that is initialization-compatible with value.
[Note 1: 
This operation possibly needs to initialize locks.
— end note]
Concurrent access to the variable being initialized, even via an atomic operation, constitutes a data race.
[Example 1: atomic<int> v = ATOMIC_VAR_INIT(5); — end example]