D1144R13
std::is_trivially_relocatable

Draft Proposal,

Author:
Audience:
WG21
Project:
ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21
Draft Revision:
74

Abstract

P1144 trivial relocation is used by Abseil, AMC, BSL, Folly, HPX, Parlay, Pocketpy, Qt, Subspace, and Thrust. Let’s make it part of the C++ standard.

1. What’s going on here?

Some of us submitted [P3236] for St Louis 2024, indicating our disapproval of [P2786R4] and asking for our preferred direction [P1144R10] to be discussed in committee. This resulted in EWG’s "clawing back" P2786 for one meeting, but did not result in any committee discussion of P1144, due to its primary author.

[P2786R13] was then merged into the Working Draft at Hagenberg 2024. Almost all of P3236’s objections remain unaddressed.

In addition, the Working Draft version of P2786R13 contains several problems that weren’t present in P2786R4 at the time we wrote [P3236].

Therefore we submit this revision of P1144, which addresses all of these problems directly, as a diff against the current Working Draft. We gladly list ourselves as coauthors of this proposal, in the same way that five Bloomberg employees listed themselves as coauthors of [P2786R13]. Unlike them, we come from a variety of backgrounds and maintain a variety of libraries.

We beg WG21 to resolve the community’s concerns before it is too late.

2. Scope and design

In this rebased proposal, we propose:

3. Proposed wording

The wording in this section is relative to the current working draft.

3.1. [cpp.predefined]

Modify the feature-test macros in the table in [cpp.predefined]:

__cpp_impl_three_way_comparison   201907L
__cpp_impl_trivially_relocatable  YYYYMML
__cpp_implicit_move               202207L

[...]

__cpp_threadsafe_static_init      200806L
__cpp_trivial_relocatability      202502L
__cpp_trivial_union               202502L

3.2. [version.syn]

Bump the feature-test macro in [version.syn]/2:

#define __cpp_lib_transparent_operators             201510L // freestanding, also in <memory>, <functional>
#define __cpp_lib_trivially_relocatable             202502L YYYYMML // freestanding, also in <memory>, <type_traits>
#define __cpp_lib_tuple_element_t                   201402L // freestanding, also in <tuple>

3.3. [basic.types.general]

Add a new section in [basic.types.general]:

9․ Arithmetic types ([basic.fundamental]), enumeration types, pointer types, pointer-to-member types ([basic.compound]), 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 Trivially copyable 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.

[Note: For a trivially relocatable type, the object relocation operations performed by, for example, std::swap_ranges or std::vector::reserve, are always tantamount to simple copies of the underlying bytes. —end note]

10․ A type is a literal type if it is: [...]

3.4. [class.pre]

Modify [class.pre] as follows:

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, the identifier final, trivially_relocatable_if_eligible, or replaceable_if_eligible, and a colon or left brace, the identifier is interpreted as a class-property-specifier.

[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 B trivially_relocatable_if_eligible : C{};
                          // OK, definition of nested class B,
                          // not declaration of a bit-field member
                          // trivially_relocatable_if_eligible
  };
end example]

3.5. [class.prop]

Modify [class.prop] as follows:

1․ A trivially copyable class is a class:

  • that has at least one eligible copy constructor, move constructor, copy assignment operator, or move assignment operator ([special], [class.copy.ctor], [class.copy.assign]),

  • where each eligible copy constructor, move constructor, copy assignment operator, and move assignment operator is trivial, and

  • that has a trivial, non-deleted destructor ([class.dtor]).

x․ A trivially relocatable class is a class:

  • where no eligible copy constructor, move constructor, copy assignment operator, move assignment operator, or destructor is user-provided,
  • which has no virtual member functions or virtual base classes,
  • all of whose non-static data members are either of reference type or of trivially relocatable type ([basic.types.general]), and
  • all of whose base classes are of trivially relocatable type;
or a class that is declared with a trivially_relocatable attribute with value true ([dcl.attr.trivreloc]) if that attribute is supported by the implementation ([cpp.cond]).

2․ A class C is default-movable if

  • overload resolution for direct-initializing an object of type C from an xvalue of type C selects a constructor that is a direct member of C and is neither user-provided nor deleted,
  • overload resolution for assigning to an lvalue of type C from an xvalue of type C selects an assignment operator function that is a direct member of C and is neither user-provided nor deleted, and
  • C has a destructor that is neither user-provided nor deleted.

3․ A class is eligible for trivial relocation unless it

  • has any virtual base classes,
  • has a base class that is not a trivially relocatable class,
  • has a non-static data member of an object type that is not of a trivially relocatable type, or
  • 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 C is a trivially relocatable class if it is eligible for trivial relocation and

  • has the trivially_relocatable_if_eligible class-property-specifier,
  • is a union with no user-declared special member functions, or
  • 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 C is eligible for replacement unless

  • it has a base class that is not a replaceable class,
  • it has a non-static data member that is not of a replaceable type,
  • overload resolution fails or selects a deleted constructor when direct-initializing an object of type C from an xvalue of type C ([dcl.init.general]),
  • overload resolution fails or selects a deleted assignment operator function when assigning to an lvalue of type C from an xvalue of type C ([expr.assign], [over.assign])), or
  • it has a deleted destructor.

7․ A class C is a replaceable class if it is eligible for replacement and

  • has the replaceable_if_eligible class-property-specifier,
  • is a union with no user-declared special member functions, or
  • is default-movable.

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]

10․ A class S is a standard-layout class if it: [...]

3.6. [cpp.cond]

Add a new entry to the table of supported attributes in [cpp.cond]:

noreturn              200809L
trivially_relocatable YYYYMML
unlikely              201803L

3.7. [dcl.attr.trivreloc]

Add a new section after [dcl.attr.nouniqueaddr]:

1․ The attribute-token trivially_relocatable specifies that a class type’s relocation operation has no visible side-effects other than a copy of the underlying bytes, as if by the library function std::memcpy. It may be applied to the definition of a class. It shall appear at most once in each attribute-list. An attribute-argument-clause may be present and, if present, shall have the form
( constant-expression )
The constant-expression shall be an integral constant expression of type bool. If no attribute-argument-clause is present, it has the same effect as an attribute-argument-clause of (true).

2․ If any definition of a class type has a trivially_relocatable attribute with value V, then each definition of the same class type shall have a trivially_relocatable attribute with value V. No diagnostic is required if definitions in different translation units have mismatched trivially_relocatable attributes.

3․ If a class type is declared with the trivially_relocatable attribute, and the program relies on observable side-effects of its relocation other than a copy of the underlying bytes, the behavior is undefined.

4․ Recommended practice: The value of a has-attribute-expression for the trivially_relocatable attribute should be 0 for a given implementation unless this attribute can cause a class type to be trivially relocatable ([class.prop]).

3.8. [expr.prim.lambda.closure]

Modify [expr.prim.lambda.closure] as follows:

3. The closure type is not an aggregate type ([dcl.init.aggr]); it is a structural type ([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:

  • the size and/or alignment of the closure type,

  • whether the closure type is trivially copyable ([class.prop]),

  • whether the closure type is trivially relocatable ([class.prop]),

  • whether the closure type is replaceable ([class.prop]), or
  • whether the closure type is a standard-layout class ([class.prop]).

An implementation shall not add members of rvalue reference type to the closure type.

3.9. [lex.name]

Modify [lex.name] as follows:

2. The identifiers in Table 4 have a special meaning when appearing in a certain context. When referred to in the grammar, these identifiers are used explicitly rather than using the identifier grammar production. Unless otherwise specified, any ambiguity as to whether a given identifier has a special meaning is resolved to interpret the token as a regular identifier.

Table 4 — Identifiers with special meaning

final     import  post  replaceable_if_eligible
override  module  pre   trivially_relocatable_if_eligible

3.10. [library.class.props]

Modify [library.class.props] as follows:

1. Unless explicitly stated otherwise, it is unspecified whether any class described in [support] through [exec] and [depr] is a trivially copyable class, a trivially relocatable 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 of trivially_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 class C is a replaceable class ([class.prop]) if assigning an xvalue a of type C to an object b of type C is semantically equivalent to destroying b and then constructing from a in b’s place.

3.11. [meta.type.synop]

Modify [meta.type.synop] as follows:

[...]
// [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_replaceable;
template<class T> struct is_standard_layout;
[...]
template<class T>
  constexpr bool is_implicit_lifetime_v = is_implicit_lifetime<T>::value;
template<class T>
  constexpr bool is_replaceable_v = is_replaceable<T>::value;
template<class T>
  constexpr bool has_virtual_destructor_v = has_virtual_destructor<T>::value;

3.12. [meta.unary.prop]

Modify Table 54 in [meta.unary.prop] as follows:

TemplateConditionPreconditions
template<class T> struct is_trivially_copyable; T is a trivially copyable type ([basic.types.general]) remove_all_extents_t<T> shall be a complete type or cv void.
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 cv void.
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 cv void.
template<class T> struct is_standard_layout; T is a standard-layout type ([basic.types.general]) remove_all_extents_t<T> shall be a complete type or cv void.

3.13. [obj.lifetime]

Modify [obj.lifetime] as follows:

template<class T>
  T* trivially_relocate(T* first, T* last, T* result);

9. Mandates: is_trivially_relocatable_v<T> && !is_const_v<T> is true. T is not an array of unknown bound.

10. Preconditions:

  • [first, last) is a valid range.

  • [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 type T.

  • No element in the range [first, last) is a potentially-overlapping subobject.

11. Postconditions: No effect if result == first is true. 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]

3.14. [diff.cpp23.dcl.dcl]

Modify [diff.cpp23.dcl.dcl] as follows:

1. Affected subclause: [dcl.decl.general]
Change: Introduction of trivially_relocatable_if_eligible and replaceable_if_eligible as 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:

struct C {};
struct C replaceable_if_eligible {};    // was well-formed (new variable replaceable_if_eligible)
                                        // now ill-formed (redefines C)
end example]

References

Informative References

[P1144R10]
Arthur O'Dwyer. std::is_trivially_relocatable. February 2024. URL: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p1144r10.html
[P2786R13]
Pablo Halpern, Joshua Berne, Corentin Jabot, Pablo Halpern, Lori Hughes. Trivial Relocatability For C++26. 14 February 2025. URL: https://wg21.link/p2786r13
[P2786R4]
Mungo Gill, Alisdair Meredith. Trivial Relocatability For C++26. 9 February 2024. URL: https://wg21.link/p2786r4
[P2959]
Alisdair Meredith. Relocation within containers. October 2023. URL: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2959r0.html
[P2967]
Alisdair Meredith; Mungo Gill. Relocation has a library interface. May 2024. URL: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2967r1.pdf
[P3236]
Alan de Freitas; Daniel Liam Anderson; Giuseppe D'Angelo; Hans Goudey; Jacques Lucke; Krystian Stasiowski; Stéphane Janel; Thiago Maciera. Please reject P2786 and adopt P1144. May 2024. URL: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3236r1.html
[P3516]
Louis Dionne; Giuseppe D'Angelo. Uninitialized algorithms for relocation. February 2025. URL: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3516r1.html