1. Changelog
-
R0 (pre-Hagenberg 2025): Initial revision.
2. Background
"Relocation" is the quantity-preserving analog of "copying." A set of trivially copyable objects can have arbitrarily many copies of their values made at arbitrary memory locations simply by putting the right bytes there. A set of trivially relocatable objects can be relocated to arbitrary memory locations simply by putting the right bytes there if and only if the mapping from source values to destination values is 1:1.
Many important libraries care about this ability to arbitrarily relocate objects via memcpy:
e.g. in
, in
, in the move-assignment operator of
, etc. These optimizations
are not always safe. These optimizations are safe only when the objects in question can have their values
arbitrarily relocated in a way tantamount to memcpy. For example, it’s not safe to optimize
into
unless
has this special property; it’s not
safe to optimize
move-assignment unless
has this special property; and so on.
lacks this property.
lacks this property.
Both P1144 and P2786 agree on this baseline truth about when optimizations are safe to perform.
Let’s call this property P. Many important libraries care about property P. They’ve written their own type-trait for P, and gate their optimizations on that type-trait.
The latest revision of [P2786R11] proposes two new traits for the Standard Library:
-
means roughly that assignment is tantamount to destroy-and-move-construct.std :: is_replaceable_v < T > -
means that destroy-and-move-construct is tantamount to memcpy.std :: is_trivially_relocatable_v < T >
P2786 points out, correctly, that a library who cares about property P can write their own type-trait as:
template < class T > constexpr bool has_property_p_v = std :: is_trivially_relocatable_v < T > && // P2786 semantics std :: is_replaceable_v < T > ;
[P1144], in contrast, has always proposed a single trait, named
,
corresponding precisely to property P:
template < class T > constexpr bool has_property_p_v = std :: is_trivially_relocatable_v < T > ; // P1144 semantics
What sorts of names do libraries actually pick, in practice, for this type-trait that I’ve been calling
?
Third-party library | Name for property P |
Abseil |
|
Amadeus AMC |
|
Bloomberg BSL |
|
Folly |
|
HPX |
|
CMU Parlay |
|
PocketPy |
|
Qt |
|
Google Skia |
|
Subspace |
|
Thrust |
|
snncpp/snn-core |
|
charles-salvia/std_error |
|
sarah-quinones/veg |
|
Abseil, Folly, HPX, Parlay, and Subspace all explicitly define their trait in terms of P1144’s
whenever the compiler advertises support for P1144. A typical
implementation goes like this:
namespace lib { // Define a trait for property P, // by the name is_trivially_relocatable. template < class T > struct is_trivially_relocatable : #if __cpp_lib_trivially_relocatable // P1144 std :: is_trivially_relocatable < T > {}; #else std :: is_trivially_copyable < T > {}; #endif } ~~~~ if constexpr ( lib :: is_trivially_relocatable < T >:: value ) { ~~~~ memcpy ~~~~ }
Notice that this relies on the Standard Library to define
with P1144 semantics. That is, if their STL vendor decides to define the feature-test macro
but to give
some other
semantics that are laxer than P1144’s — for example, to give it P2786R11’s proposed semantics instead —then this library will mis-optimize. For example, it will use memswap to swap objects
that can’t be swapped that way. It will use memmove to shift objects (e.g. in
)
that can’t be shifted that way.
P2786 basically asks that all third-party libraries should "fix themselves" by changing their code in one of two ways: Either they’ll have to modify their definition of the property-P trait, like this:
namespace lib { // Define a trait for property P, // by the name is_trivially_relocatable. template < class T > struct is_trivially_relocatable : #if __cplusplus >= 20XXYYL // P2786 // Unfortunately the STL's version is weaker than ours std :: bool_constant < std :: is_trivially_relocatable_v < T > && std :: is_replaceable_v < T >> {}; #else std :: is_trivially_copyable < T > {}; #endif } ~~~~ if constexpr ( lib :: is_trivially_relocatable < T >:: value ) { ~~~~ memcpy ~~~~ }
Or else they’ll have let their type-trait’s own meaning shift from property P to the weaker property Q, and carefully modify every call-site, like this:
namespace lib { // Define a trait for property Q (no longer P), // by the name is_trivially_relocatable. template < class T > struct is_trivially_relocatable : #if __cplusplus >= 20XXYYL // P2786 std :: is_trivially_relocatable < T > {}; #else std :: is_trivially_move_constructible < T > {}; #endif template < class T > struct is_replaceable : #if __cplusplus >= 20XXYYL // P2786 std :: is_replaceable < T > {}; #else std :: is_trivially_copyable < T > {}; #endif } ~~~~ if constexpr ( lib :: is_trivially_relocatable < T >:: value && lib :: is_replaceable < T >:: value ) { ~~~~ memcpy ~~~~ }
Neither of these are appealing solutions.
Even worse, if neither solution is applied (or a solution is applied inconsistently),
then the clients of
will find themselves memcpying objects with property Q in
situations where property P is required for safety — i.e., in situations where memcpy
has the wrong physical behavior — leading to wild pointers, the eliding of important side-effects,
or worse.
We should not put a trait named
into the Standard Library unless it has the same meaning as today’s library vendors understand
by
. That is, property P.
3. Libraries overwhelmingly prefer a single trait
In June 2024, Arthur catalogued 21 libraries that use traits or algorithms relating in some way to "trivial relocation."
13 of these libraries define a single trait for property P (that is, P1144 semantics). Of those, 8 of them explicitly refer to P1144 and implement the set of library algorithms and/or container optimizations that P1144 proposes. 5 libraries (listed in the timeline below) use P1144’s feature-test macros.
Vice versa, 2 libraries define a single trait for property Q (that is, P2786 semantics):
these are Thermadiag/seq and OleErikPeistorpet/OE-Lib. Their traits are true for
,
although it’s unclear if every use of these traits is correct.
As for standard libraries themselves: Both libc++ and libstdc++ define private traits
which use P1144 semantics (they are false for
), but currently use their traits
only to optimize vector reallocation, so they remain safe if either P1144 or P2786 is chosen.
Microsoft STL does not perform any relocation optimizations.
No library ever defines anything corresponding to P2786’s
as a standalone trait.
No library defines both a trait for property P and one for property Q. That is, the 13 libraries that care about P do not seem to care about Q; and the 2 libraries that seem to care about Q do not seem to care about P. We can define a trait that captures most (but not all) of property Q...
namespace lib { template < class T > struct has_property_q : std :: bool_constant < has_property_p < T > || std :: is_trivially_move_constructible_v < T >> {}; } static_assert ( lib :: has_property_q < std :: tuple < int &>> );
...but none of the 13 libraries above seem to care about capturing that particular set of semantics; they’re happy to capture property P alone. P is the property most salient to their optimizations. We should give property P the good name.
4. Timeline of P2786 and P1144
The committee history, as I understand it, goes like this:
2018 | Oct | P1144 is published |
2020 | Feb | P1144 voted forward by EWGI (1–3–4–1–0); remains in EWGI |
2023 | Feb | P2786 is published P1144 voted forward by EWGI (0–7–4–3–1); remains in EWGI P2786 voted forward by EWGI (1–8–3–3–1); goes to EWG |
2024 | Feb | P2786 voted forward by EWG (7–9–6–0–2); goes to CWG |
Apr | [P3233] and [P3236] are published | |
Jun | P2786 voted backward by EWG (21–15–3–6–5); goes back to EWG | |
Nov | P2786 voted sideways by EWG (10-10-2-7-2); goes to LEWG P2786 voted sideways by LEWG (11-4-0-1-8); goes to EWG |
The implementation history goes like this. For our purposes here, "P1144 semantics" means
simply that a type with a funky assignment operator (such as
) is not trivially relocatable;
"P2786 semantics" means simply that it is.
2012 | Dec | Facebook Folly adds with P1144 semantics
|
2013 | Bloomberg BSL uses with P1144 semantics
| |
2015 | Jul | Qt adds with P1144 semantics
|
Sep | OleErikPeistorpet/OE-Lib adds with P1144 semantics
| |
2018 | Jul | Arthur O’Dwyer implements P1144 in a Clang fork, available on godbolt.org (full libc++ and libstdc++ support) |
Oct | P1144R0 is published libstdc++ adds with P1144 semantics
| |
Nov | OleErikPeistorpet/OE-Lib changes from P1144 to P2786 semantics | |
2021 | Feb | sarah-quinones/veg adds with P1144 semantics
|
Apr | Amadeus AMC uses with P1144 semantics
| |
2022 | May | Subspace adds with P2786 semantics
|
Sep | snncpp/snn-core uses with P1144 semantics
| |
Oct | Google Skia adds with P1144 semantics
| |
2023 | Jan | Subspace changes from P2786 to P1144 semantics |
Feb | P2786R0 is published | |
Jul | Ste||ar HPX explores both P2786 and P1144 during GSoC 2023 | |
Oct | Quuxplusone/SG14 uses P1144’s feature-test macro | |
Nov | charles-salvia/std_error adds P1144’s feature-test macro | |
2024 | Jan | Ste||ar HPX adds with P1144 semantics
|
Feb | libc++ adds with compiler-dependent semanticsCMU Parlay adds P1144’s feature-test macro Pocketpy adds with P1144 semanticsGoogle Abseil adds P1144’s feature-test macro Corentin Jabot implements P2786 in a Clang fork, available on godbolt.org (no library support) | |
Mar | Amirreza Ashouri proposes Clang’s builtin should have P1144 semantics.
Commenters in favor include AMC, HPX, Parlay, Qt, and Subspace
| |
Apr | P3236R0 is published. Signatories include AMC, Blender, Folly, HPX, Parlay, and Qt | |
Jun | Facebook Folly adds P1144’s feature-test macro | |
Aug | ultimatepp/ultimatepp adds with P1144 semantics
|
5. Learn more about trivial relocation
Besides Arthur’s blog, you might also want to look at:
-
"Trivially relocatable types in C++/Subspace", by Dana Jansens
-
"Qt and trivial relocation", by Giuseppe D’Angelo, on the KDAB blog
-
"Relocation semantics in the HPX library", by Isidoros Tsaousis
-
"Object relocation" in the Folly repository