Date: Fri, 23 Jan 2026 23:48:21 +0000
On Fri, Jan 23, 2026 at 11:24 PM Arthur O'Dwyer wrote
>
> 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.)
> assert(memcmp(&a, &b, sizeof(Animal)) != 0); // invariably true
That 'memcmp' operation will always report they're the same byte
pattern if either:
a) The Animal class is final
or:
b) The Animal object we're dealing with is the most-derived object
When we have a vector full of elements, each element is always the
most-derived type. Therefore, a polymorphic vector element can be
memcpy'd so long as you're not on Apple Silicon. On every machine
other than arm64e, you can memcpy a polymorphic object so long as the
class is 'final', or you're guaranteed to be working with the
most-derived object.
That is why is_trivially_copy_constructible_v<Poly> should really be
is_trivially_copy_constructible_v<Poly, true>. That second template
parameter, i.e. a boolean of value 'true', indicates that you're
dealing with the most-derived object. This should evaluate to true so
that we can use 'memcpy'. But on Apple Silicon it would evaluate to
false.
>
> 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.)
> assert(memcmp(&a, &b, sizeof(Animal)) != 0); // invariably true
That 'memcmp' operation will always report they're the same byte
pattern if either:
a) The Animal class is final
or:
b) The Animal object we're dealing with is the most-derived object
When we have a vector full of elements, each element is always the
most-derived type. Therefore, a polymorphic vector element can be
memcpy'd so long as you're not on Apple Silicon. On every machine
other than arm64e, you can memcpy a polymorphic object so long as the
class is 'final', or you're guaranteed to be working with the
most-derived object.
That is why is_trivially_copy_constructible_v<Poly> should really be
is_trivially_copy_constructible_v<Poly, true>. That second template
parameter, i.e. a boolean of value 'true', indicates that you're
dealing with the most-derived object. This should evaluate to true so
that we can use 'memcpy'. But on Apple Silicon it would evaluate to
false.
Received on 2026-01-23 23:47:27
