Moving via relocate-and-construct
A slow-moving StackOverflow discussion thread
alerted me to this interesting application of P1144 std::relocate
(Godbolt):
On libc++ and MSVC, std::array<std::string, 1000> is a trivially relocatable type;
but it’s not trivially move-constructible, because string’s move-constructor has to null
out the source object. Moving a single string is tantamount to a memcpy
plus a memset (to null out the source). So, move-constructing an array<string, 1000> is tantamount
to a 1000-times-larger memcpy plus a 1000-times-larger memset. But no mainstream compiler
is smart enough to fission the loop body in that way.
P1144 std::relocate
gives us a name for array<string>’s operation that is tantamount to memcpy; and we already have a name
(“default constructor”) for array<string>’s operation that is tantamount to memset.
So — here’s the cool part — we can help our insufficiently-smart compiler see the right way
to factor this code by writing the move-construction as a relocate plus a placement-new.
using A = std::array<std::string, 1000>;
A *move_to_heap(A& a) {
// Compiler fails to fission the loop
return new A(std::move(a));
}
A *move_to_heap(A& a) {
// Compiler generates memcpy + memset
auto *p = new A(std::relocate(&a));
::new (&a) A;
return p;
}
Of course this trick isn’t general-purpose. It does work for non-trivially relocatable types
(for which std::relocate rightly won’t generate a memcpy); but it doesn’t compile for types that aren’t default-constructible,
nor does it work for types whose default constructor has unwanted side effects, or whose moved-from state is distinguishable
from their default-constructed state. (As usual, std::pmr::string is an example.) Therefore
I wouldn’t expect to see this trick in generic code.
But this is a neat application of the P1144 high-level primitives (is that an oxymoron?)
to help the compiler see how to generate optimal code, without throwing up our hands and descending
into undefined-behavior-land. I can’t think of a “standard-compliant” way to get the memcpy+memset
codegen in today’s C++ — but with P1144’s vocabulary, it’s straightforward.
