Polymorphic types aren’t trivially relocatable
One of the (minor, non-key) differences between P1144 trivial relocatability and P2786R1 trivial relocatability (§11.3, page 19) is that P2786R1 accidentally considers polymorphic types to be trivially relocatable by default. I say “accidentally” because of course this cannot really be permitted. Godbolt:
struct B {
virtual int getter() const { return 1; }
virtual ~B() = default;
};
struct D : B {
int d = 2;
int getter() const override { return d; }
};
alignas(D) char dbuf[sizeof(D)];
alignas(B) char bbuf[sizeof(B)];
B *d = ::new (dbuf) D();
B *b = (B*)bbuf;
// RELOCATE
printf("Expect 1: %d\n", b->getter());
b->~B();
The “RELOCATE
” comment should be replaced with the code to relocate a B
object from *d
into *b
.
In P1144, the spelling is simply
std::relocate_at(d, b);
and the library decides whether to use trivial relocation behind the scenes. In P2786R1, the spelling is
static_assert(std::is_trivially_relocatable_v<B>);
std::trivially_relocate(d, d+1, b);
In P1144, B
is not considered trivially relocatable, and the code above Just Works.
Why is
B
not trivially relocatable? P1144 offers two answers: One, at the compiler level,B
doesn’t meet [basic.types.general]’s criterion that a trivially relocatable type mustn’t have virtual member functions. Two, at the human level, we can see thatB
fails to model [concept.relocatable]’s semantic requirements.
In P2786R1, B
is (“accidentally”) considered trivially relocatable, and the code above has undefined behavior
at runtime.
*d
’s vptr is memmoved into*b
, and then the virtual callb->getter()
invokesD::getter
through that (now-erroneous) vptr, which tries to access the data memberd
of aD
object that doesn’t actually exist.
The Godbolt above uses my own Clang/libc++ implementation of P1144R8, and Corentin Jabot’s Clang implementation of P2786R0 (with none of the library components). I’ve told Corentin he should feel free to adopt any pieces of my libc++ implementation that would help get the P2786 demonstration into a working state. Of course, the authors of P2786 could also help by refactoring P2786’s library clauses to match P1144’s. :) As you can see, the P1144 library facilities tend to be more usable than P2786’s.
This might be a good time to remind folks that my Clang/libc++ implementation also provides
a [[clang::maybe_trivially_relocatable]]
attribute implementing P2786’s
“dull knife” semantics (but without P2786R1’s
sharp-when-you-didn’t-want-it parts, like its treatment of polymorphic types). So you can experiment
with a full libc++ implementation of dull-knife semantics simply by passing
-D_LIBCPP_TRIVIALLY_RELOCATABLE_IF(x)=[[clang::maybe_trivially_relocatable(x)]]
.
(Godbolt.)