C++ Logo

std-proposals

Advanced search

Re: [std-proposals] is_trivially_copyable_in_reality

From: Lénárd Szolnoki <cpp_at_[hidden]>
Date: Sat, 24 Jan 2026 17:43:48 +0000
On 23 January 2026 23:24:21 GMT, Arthur O'Dwyer via Std-Proposals <std-proposals_at_[hidden]> wrote:
>On Fri, Jan 23, 2026 at 5:20 PM Frederick Virchanza Gotham via
>Std-Proposals <std-proposals_at_[hidden]> wrote:
>
>> [...]
>
>As an optimisation for B, I want to use 'memcpy' instead of running a
>> loop with 'construct_at'. But in order to be able to determine when I
>> can use this optimisation, I need a new trait. Right now the Standard
>> only has 'is_trivially_copyable' for this purpose
>
>
>Wrong.
>C++ has many "is_trivially_fooable" type traits, for various verbs "foo".
>They come in two flavors:
>- Surgical traits: is_trivially_copy_constructible,
>is_trivially_move_assignable, is_trivially_destructible, (Clang's
><https://reviews.llvm.org/D147175> is_trivially_equality_comparable,
>Giuseppe's
><https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2023/p2782r0.html>
>is_trivially_value_initializable, ...)
>- Holistic traits: is_trivially_copyable, (P1144's
><https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p1144r13.html>
> is_trivially_relocatable)
>
>The "surgical" traits are — well, caveat: they're currently not exactly,
>but they're basically *intended* to have been and *are currently used in
>the wild* as — simple optimization gates. They're aimed at the
>user-programmer who says, "Okay, my code wants to do this specific
>operation on a type (e.g., copy-construct it; e.g. compare it for
>equality). Can I get away with doing that specific operation on the *object
>representation* (e.g. memcpy it; e.g. memcmp it) instead?" The poster
>children for these traits are all in the STL:
>- is_trivially_copy_assignable controls whether std::copy can memcpy
>- is_trivially_move_assignable controls whether std::move
><https://en.cppreference.com/w/cpp/algorithm/move.html> can memcpy
>- is_trivially_copy_constructible controls whether std::uninitialized_copy
>can memcpy
>- is_trivially_move_constructible controls whether std::uninitialized_move
>can memcpy
>- is_trivially_equality_comparable controls whether std::equal can memcmp
>- is_trivially_default_constructible controls whether
>std::uninitialized_default_construct can memset
>- is_trivially_value_initializable controls whether
>std::uninitialized_value_construct can memset
>
>In your case, you are precisely asking "Can I replace copy-construction
>with memcpy?" So you should ask is_trivially_copy_constructible_v<T>.
>(Now, currently is_trivially_copy_constructible_v<T> reports false for
>types with non-trivial destructors. This is the subject of LWG2827
><https://cplusplus.github.io/LWG/issue2827> and P2842R0
><https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2842r0.pdf>,
>and falls under my "currently not exactly" caveat.)
>
>The "holistic" traits capture complex holistic properties of a type. Right
>now there's only one — is_trivially_copyable. This is aimed at the
>user-programmer who says, "Okay, my code wants to do some complicated
>rearrangement, copying, and destruction of a bunch of objects, and I don't
>want to restrict myself as to exactly what *specific* operations I'm going
>to pretend to do. I just want to know, can I get away with doing such a
>complicated operation on the *object representations* of these objects?"
>For example, I'm going to do something like std::partial_sort_copy — can I
>copy and shuffle the objects as if they were just bags of bytes?
>is_trivially_copyable tells me the answer.
>
>is_trivially_relocatable (non-standard, but used by many libraries in real
>life, including Abseil, Folly, HPX, Parlay, and Thrust) answers the same
>question but specifically for complicated *affine (one-to-one)*
>rearrangements of values. For example, I'm going to std::sort these
>objects, or std::rotate them, or std::partition them — can I permute their
>object representations instead of calling their assignment operators and
>swap and so on? I'm going to reallocate this buffer of objects from buffer
>A to buffer B — can I memcpy them instead of calling their move
>constructors and destructors? I'm going to std::partial_sort_copy these
>objects from A to B, and then destroy from A exactly those objects whose
>values I've copied into B — can I do that just by shuffling bytes?
>is_trivially_relocatable tells me the answer.
>In particular, is_trivially_relocatable tells you whether you can *safely*
>lower std::sort to std::qsort (although it doesn't help with the icky
>impedance mismatch between sort's and qsort's comparator signatures).
>
>
>As for the name of this new trait, well the word 'trivial' is no
>> longer usable in any context in C++ because of the extreme ambiguity
>> that has been given to it -- remember how we were gonna say an object
>> was 'trivially relocatable' even if you needed to run an encryption
>> algorithm after relocating it? And we called that encryption algorithm
>> "restart_lifetime"? So forget about the word 'trivial', it's useless
>> now.
>>
>
>FWIW, I disagree; I think the Kona vote proved that an overwhelming
>majority of WG21 *rejected* the attempt to make "trivial" mean something
>other than what it means; if anything, we can say that the meaning it's
>already got in popular usage has been *recently reaffirmed*.
>Compare [class.union.general]
><https://eel.is/c++draft/class.union.general#note-3> and
>[class.copy.ctor]/10.1 <https://eel.is/c++draft/class.copy.ctor#10.1>,
>where "trivial" is used with exactly the "bitwise" meaning; and P3074
>"Trivial Unions"
><https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3074r7.html>
>(adopted for C++26), where "trivial destructor" is used to mean exactly
>"destructor that does nothing" (not, e.g. "destructor that does encryption
>algorithms").
>
>
>[...] Let's call this new trait: is_memcpyable
>>
>
>No. That's like calling a mushroom is_edible ("Any mushroom is edible...
>once"), or a spirit from the vasty deep is_callable ("But will they come
>when you do call for them?"). Any typ*e is memcpyable*; but what does it do
>when you do memcpy it?
>If memcpying it is a practical substitute for copy-constructing it, then it
>is *trivially copy constructible*.
>If memcpying it is a practical substitute for move-assigning it, then
>it is *trivially
>move assignable*.
>We already have these traits. Admittedly we (WG21) desperately need to
>clean them up around the edges, but we do already *have* them. You (the
>user-programmer) just have to start using them.
>
>Now, this does leave you with the question: "Should
>`is_trivially_copy_constructible_v<Poly>` ever be true?" Sadly the answer
>is no, because a polymorphic type can have a copy constructor that changes
>the vptr. I'm not talking about ARM64e stuff here — just simple inheritance.
> struct Animal { virtual int f(); };
> struct Cat : Animal { int f() override; };
> const Animal& a = Cat();
> Animal b = a;
>Copying from `a` to `b` isn't a simple memcpy on *any* platform. (Godbolt.
><https://godbolt.org/z/nGbYd8Yo1>)
> assert(memcmp(&a, &b, sizeof(Animal)) != 0); // invariably true

I don't think you are currently allowed to memcpy from/to potentially overlapping subobjects, even if they are trivially copyable. Although I don't understand why we ban them as source objects.

https://eel.is/c++draft/basic#types.general-3

>
>HTH,
>Arthur

Received on 2026-01-24 17:43:56