| Document #: | P3920R0 [Latest] [Status] |
| Date: | 2025-11-07 |
| Project: | Programming Language C++ |
| Audience: |
EWG, LWG, CWG |
| Reply-to: |
Louis Dionne <ldionne.2@gmail.com> |
In a joint EWG and LEWG session in Kona, there was consensus to remove the trivial relocation feature (P2786R13) from C++26 due to various issues that won’t be repeated here, with a desire to fix these issues in the C++29 time frame. This paper contains the wording for the removal, based on top of the current working draft.
Revert P2786:
[class.pre]
Satisfy expectations that trivially relocatably types are
memcpy-ableRemove the std::relocate
function:
[obj.lifetime]
Remove std::relocateRespecify using memcpy rules:
[basic.types.general],
11.2 [class.prop],
20.2.6 [obj.lifetime]
Permit trivial relocation via
memcpy[basic.types.general]
Trivial relocatability should imply bitwise relocatabilitymemcpyFix the special tokens:
[class.pre]
Conditional trivially relocatable types[lex.key]
Make *_if_eligible real
keywords*_if_eligible
keywordsSupport more types:
[obj.lifetime]
Allow const-qualified types for
relocate[obj.lifetime]
Allow const-qualified types for
trivially_relocateFix implicit property:
[class.prop]
Implicitly deleted move operation should not disable trivial relocation
CWG3049Adopt uninitialized relocation algorithm:
std::uninitialized_relocate
(P3516)[specialized.algorithms.general]
P3516 Adopt Uninitialized algorithms for relocation (for C++26)Adopt new lifetime management primitive:
[obj.lifetime]
Add a start_lifetime_at function
P3858[obj.lifetime]
Add a start_lifetime_at
functionThis wording does a partial revert of what was added by P2786R13. Some drive-by improvements made in P2786R13 are conserved, and this paper calls them out.
This paper also removes a few additions that were made with Reflection (P2996R13) in Sofia.
Table 4: Identifiers with special meaning [tab:lex.name.special]:
final import post |
override module pre |
9 Arithmetic types ([basic.fundamental]), enumeration types, pointer types, pointer-to-member types ([basic.compound]),
std::meta::info,std::nullptr_t, and cv-qualified versions of these types are collectively called scalar types. Scalar types, trivially copyable class types ([class.prop]), arrays of such types, and cv-qualified versions of these types are collectively called trivially copyable types.Scalar types, trivially relocatable class types ([class.prop]), arrays of such types, and cv-qualified versions of these types are collectively called trivially relocatable types. Cv-unqualified scalar types, replaceable class types ([class.prop]), and arrays of such types are collectively called replaceable types.Scalar types, standard-layout class types ([class.prop]), arrays of such types, and cv-qualified versions of these types are collectively called standard-layout types. Scalar types, implicit-lifetime class types ([class.prop]), array types, and cv-qualified versions of these types are collectively called implicit-lifetime types.
4 The closure type is not an aggregate type (9.5.2 [dcl.init.aggr]); it is a structural type (13.2 [temp.param]) if and only if the lambda has no lambda-capture. An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing
(4.1) — the size and/or alignment of the closure type,
(4.2) — whether the closure type is trivially copyable (11.2 [class.prop]), or
(4.3) — whether the closure type is trivially relocatable (11.2 [class.prop]),
(4.4) — whether the closure type is replaceable (11.2 [class.prop]), or(4.x) — whether the closure type is a standard-layout class (11.2 [class.prop]).
When applying the wording for P2786R13, improvements were made to how the class specifiers are defined. CWG expressed a desire to keep those improvements, so this is not a pure revert of the changes added by P2786R13.
1 […]
class-head:
class-key attribute-specifier-seqopt class-head-name class-property-specifier-seqopt base-clauseopt
class-key attribute-specifier-seqopt base-clauseopt
class-head-name:
nested-name-specifieropt class-name
class-property-specifier-seq:
class-property-specifier class-property-specifier-seqopt
class-property-specifier:
final
trivially_relocatable_if_eligible
replaceable_if_eligible
[…]
5 Each class-property-specifier shall appear at most once within a single class-property-specifier-seq. Whenever a class-key is followed by a class-head-name,
one of the identifiersthe identifierfinal,trivially_relocatable_if_eligible, orreplaceable_if_eligiblefinal, and a colon or left brace, the identifier is interpreted as a class-property-specifier.
[ Example:— end example ]struct A; struct A final {}; // OK, definition of struct A, // not value-initialization of variable final struct X { struct C { constexpr operator int() { return 5; } }; struct Btrivially_relocatable_if_eligiblefinal : C{}; // OK, definition of nested class B, // not declaration of a bit-field member //trivially_relocatable_if_eligiblefinal };
2 A class C is default-movable if
- (2.1) overload resolution for direct-initializing an object of type
Cfrom an xvalue of typeCselects a constructor that is a direct member ofCand is neither user-provided nor deleted,- (2.2) overload resolution for assigning to an lvalue of type
Cfrom an xvalue of typeCselects an assignment operator function that is a direct member of `C and is neither user-provided nor deleted, and- (2.3)
Chas a destructor that is neither user-provided nor deleted.3 A class is eligible for trivial relocation unless it
- (3.1) has any virtual base classes,
- (3.2) has a base class that is not a trivially relocatable class,
- (3.3) has a non-static data member of an object type that is not of a trivially relocatable type, or
- (3.4) has a deleted destructor, except that it is implementation-defined whether an otherwise-eligible union having one or more subobjects of polymorphic class type is eligible for trivial relocation.
4 A class
Cis a trivially relocatable class if it is eligible for trivial relocation and
- (4.1) has the
trivially_relocatable_if_eligibleclass-property-specifier,- (4.2) is a union with no user-declared special member functions, or
- (4.3) is default-movable.
5 [ Note: A class with const-qualified or reference non-static data members can be trivially relocatable. — end note ]
6 A class
Cis eligible for replacement unless
- (6.1) it has a base class that is not a replaceable class,
- (6.2) it has a non-static data member that is not of a replaceable type,
- (6.3) overload resolution fails or selects a deleted constructor when direct-initializing an object of type
Cfrom an xvalue of typeC([dcl.init.general]),- (6.4) overload resolution fails or selects a deleted assignment operator function when assigning to an lvalue of type
Cfrom an xvalue of typeC([expr.assign], [over.assign]), or- (6.5) it has a deleted destructor.
7 A class
Cis a replaceable class if it is eligible for replacement and
8 [ Note: Accessibility of the special member functions is not considered when establishing trivial relocatability or replaceability. — end note ]
9 [ Note: Not all trivially copyable classes are trivially relocatable or replaceable. — end note ]
Table 22 — Feature-test macros [tab:cpp.predefined.ft]
Macro name Value… … __cpp_threadsafe_static_init200806L__cpp_trivial_relocatability202502L__cpp_trivial_union202502L… …
1 Affected subclause: [dcl.decl.general]
Change: Introduction oftrivially_relocatable_if_eligibleandreplaceable_if_eligibleas identifiers with special meaning ([lex.name]).
Rationale: Support declaration of trivially relocatable and replaceable types ([class.prop]).
Effect on original feature: Valid C++ 2023 code can become ill-formed.[ Example:— end example ]struct C {}; struct C replaceable_if_eligible {}; // was well-formed (new variable replaceable_if_eligible) // now ill-formed (redefines C)
P2786R13 also did a drive-by
fix to add an entry for
final in
[diff.cpp03.dcl.dcl].
We are not undoing that drive-by fix in this paper.
16.4.6.11 [library.class.props] Properties of library classes
1 Unless explicitly stated otherwise, it is unspecified whether any class described in [support] through [exec] and [depr] is a trivially copyable class, a standard-layout class, or an implicit-lifetime class ([class.prop]).
2 Unless explicitly stated otherwise, it is unspecified whether any class for which trivial relocation (i.e., the effects oftrivially_relocate([obj.lifetime])) would be semantically equivalent to move-construction of the destination object followed by destruction of the source object is a trivially relocatable class ([class.prop]).
3 Unless explicitly stated otherwise, it is unspecified whether a classCis a replaceable class ([class.prop]) if assigning an xvalueaof typeCto an objectbof typeCis semantically equivalent to destroyingband then constructing fromainb’s place.
Note that paragraph 1 was also added by P2786R13, but we are not removing it since it appears to be an intentional drive-by change.
// [meta.unary.prop], type properties template<class T> struct is_const; template<class T> struct is_volatile; template<class T> struct is_trivially_copyable;template<class T> struct is_trivially_relocatable;template<class T> struct is_standard_layout; template<class T> struct is_empty; template<class T> struct is_polymorphic; template<class T> struct is_abstract; template<class T> struct is_final; template<class T> struct is_aggregate; template<class T> struct is_consteval_only; ... template<class T> struct is_nothrow_destructible;template<class T> struct is_replaceable;... // [meta.unary.prop], type properties template<class T> constexpr bool is_const_v = is_const<T>::value; template<class T> constexpr bool is_volatile_v = is_volatile<T>::value; template<class T> constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;template<class T> struct is_nothrow_relocatable;template<class T>template<class T> constexpr bool is_standard_layout_v = is_standard_layout<T>::value; ... template<class T> constexpr bool is_implicit_lifetime_v = is_implicit_lifetime<T>::value;constexpr bool is_trivially_relocatable_v = is_trivially_relocatable<T>::value;template<class T>template<class T> constexpr bool has_virtual_destructor_v = has_virtual_destructor<T>::value; ... template<class T> constexpr bool is_nothrow_destructible_v = is_nothrow_destructible<T>::value;constexpr bool is_replaceable_v = is_replaceable<T>::value;template<class T>template<class T> constexpr bool is_implicit_lifetime_v = is_implicit_lifetime<T>::value; ...constexpr bool is_nothrow_relocatable_v = is_nothrow_relocatable<T>::value;
Table 54 — Type property predicates [tab:meta.unary.prop]
Template Condition Preconditions… template<class T> struct is_trivially_relocatable;T is a trivially relocatable type ([basic.types.general])
remove_all_extents_t<T>shall be a complete type or cvvoid.template<class T> struct is_replaceable;T is a replaceable type ([basic.types.general])
remove_all_extents_t<T>shall be a complete type or cvvoid.template<class T> struct is_nothrow_relocatable;
is_trivially_relocatable_v<T> || (is_nothrow_move_constructible_v<remove_all_extents_t<T>> && is_nothrow_destructible_v<remove_all_extents_t<T>>)
remove_all_extents_t<T>shall be a complete type or cvvoid.…
// [obj.lifetime], explicit lifetime management template<class T> T* start_lifetime_as(void* p) noexcept; // freestanding template<class T> const T* start_lifetime_as(const void* p) noexcept; // freestanding template<class T> volatile T* start_lifetime_as(volatile void* p) noexcept; // freestanding template<class T> const volatile T* start_lifetime_as(const volatile void* p) noexcept; // freestanding template<class T> T* start_lifetime_as_array(void* p, size_t n) noexcept; // freestanding template<class T> const T* start_lifetime_as_array(const void* p, size_t n) noexcept; // freestanding template<class T> volatile T* start_lifetime_as_array(volatile void* p, size_t n) noexcept; // freestanding template<class T> const volatile T* start_lifetime_as_array(const volatile void* p, // freestanding size_t n) noexcept;template<class T>T* trivially_relocate(T* first, T* last, T* result); // freestandingtemplate<class T>constexpr T* relocate(T* first, T* last, T* result); // freestanding
template<class T> T* trivially_relocate(T* first, T* last, T* result);9 Mandates:
is_trivially_relocatable_v<T> && !is_const_v<T>istrue.Tis not an array of unknown bound.10 Preconditions:
- (10.1)
[first, last)is a valid range.- (10.2)
[result, result + (last - first))denotes a region of storage that is a subset of the region reachable through result ([basic.compound]) and suitably aligned for the typeT.- (10.3) No element in the range
[first, last)is a potentially-overlapping subobject.11 Postconditions: No effect if
result == firstistrue. Otherwise, the range denoted by[result, result + (last - first))contains objects (including subobjects) whose lifetime has begun and whose object representations are the original object representations of the corresponding objects in the source range[first, last)except for any parts of the object representations used by the implementation to represent type information ([intro.object]). If any of the objects has union type, its active member is the same as that of the corresponding object in the source range. If any of the aforementioned objects has a non-static data member of reference type, that reference refers to the same entity as does the corresponding reference in the source range. The lifetimes of the original objects in the source range have ended.12 Returns:
result + (last - first).13 Throws: Nothing.
14 Complexity: Linear in the length of the source range.
15 Remarks: The destination region of storage is considered reused ([basic.life]). No constructors or destructors are invoked.
[ Note: Overlapping ranges are supported. — end note ]
template<class T> constexpr T* relocate(T* first, T* last, T* result);16 Mandates:
is_nothrow_relocatable_v<T> && !is_const_v<T>istrue.Tis not an array of unknown bound.17 Preconditions:
- (17.1)
[first, last)is a valid range.- (17.2)
[result, result + (last - first))denotes a region of storage that is a subset of the region reachable through result ([basic.compound]) and suitably aligned for the typeT.- (17.3) No element in the range
[first, last)is a potentially-overlapping subobject.18 Effects:
- (18.1) If
result == firstistrue, no effect;- (18.2) otherwise, if not called during constant evaluation and
is_trivially_relocatable_v<T>istrue, then has effects equivalent to:trivially_relocate(first, last, result);- (18.3) otherwise, for each integer
iin[0, last - first),19 Returns:
result + (last - first).20 Throws: Nothing.
[ Note: Overlapping ranges are supported. — end note ]
2 Affected subclause: [res.on.macro.definitions]
Change: Additional restrictions on macro names.
Rationale: Avoid hard to diagnose or non-portable constructs.
Effect on original feature: Names of special identifiers may not be used as macro names. Valid C++ 2023 code that definesreplaceable_if_eligibleortrivially_relocatable_if_eligibleas macros is invalid in this revision of C++.
2 Each of the macros defined in
<version>is also defined after inclusion of any member of the set of library headers indicated in the corresponding comment in this synopsis.[ Note: Future revisions of this document might replace the values of these macros with greater values. — end note ]
... #define __cpp_lib_transformation_trait_aliases 201304L // freestanding, also in <type_traits> #define __cpp_lib_transparent_operators 201510L // freestanding, also in <memory>, <functional>#define __cpp_lib_tuple_element_t 201402L // freestanding, also in <tuple> #define __cpp_lib_tuple_like 202311L // also in <utility>, <tuple>, <map>, <unordered_map> ...#define __cpp_lib_trivially_relocatable 202502L // freestanding, also in <memory>, <type_traits>
// associated with [meta.unary.prop], type properties consteval bool is_const_type(info type); consteval bool is_volatile_type(info type); consteval bool is_trivially_copyable_type(info type);consteval bool is_trivially_relocatable_type(info type);consteval bool is_standard_layout_type(info type); consteval bool is_empty_type(info type); ... consteval bool is_nothrow_destructible_type(info type);consteval bool is_replaceable_type(info type);consteval bool is_nothrow_relocatable_type(info type);
// associated with [meta.unary.prop], type properties consteval bool is_const_type(info type); consteval bool is_volatile_type(info type); consteval bool is_trivially_copyable_type(info type);consteval bool is_trivially_relocatable_type(info type);consteval bool is_standard_layout_type(info type); consteval bool is_empty_type(info type); ... consteval bool is_nothrow_destructible_type(info type);consteval bool is_replaceable_type(info type);consteval bool is_nothrow_relocatable_type(info type);
Thanks to Alisdair Meredith for reviewing the wording in this paper and for providing the list of NB comment dispositions.