Suppose you have a struct
with elements which you don't want to (can't) initialize in the initialization phase, but is not default initializable.
I figured out a way of using std::launder
:
Allocate a space for the structure, and then use the launder to assign a value at the right place. For example,
#include <cassert>
#include <cstddef>
#include <iostream>
#include <new>
struct T {
int t;
};
struct S {
int x;
T t;
S() = delete;
S(int x) : x(x) {}
};
int main() {
alignas(S) std::byte storage[sizeof(S)];
S s0(42);
// 0. I believe that the commented line is dangerous, although it compiles well.
// *std::launder(reinterpret_cast<S*>(storage)) = s0;
S *ps = std::launder(reinterpret_cast<S*>(storage));
ps->x = 42;
ps->t = T{};
{ // 1. Is this safe?
S &s1 = *ps;
std::cout << s1.x << std::endl;
s1.t.t = 24;
assert(s1.x == 42 && s1.t.t == 24);
}
{ // 2. Is this safe?
S s2 = *ps;
std::cout << s2.x << std::endl;
assert(s2.x == 42 && s2.t.t == 24);
}
{ // 3. Is this safe?
S s3 = std::move(*ps);
std::cout << s3.x << std::endl;
assert(s3.x == 42 && s3.t.t == 24);
}
}
The problem with this method is that it doesn't really 'construct' a structure, and therefore will not call a destructor.
Thanks.
For std::launder
to have defined behavior there has to actually be an object of the given type at the location pointed to. That is not the case in this instance, so the behavior of your program is undefined.
Consider if S::t
was a std::string
instead of a simple int
. It would be completely uninitialized and so assigning to it would likely cause it to copy data into what it thinks is its storage buffer but is actually a random, un-owned location in memory.
Simply put, std::launder
is the wrong tool for this job. You want placement-new:
int main() {
alignas(S) std::byte storage[sizeof(S)];
S *ps = new(storage) S(42);
ps->t = T{};
}