A. If I have misunderstood the standard, and there is any UB here, please kindly point it out (or alternatively confirm that there is no UB)
B. Are the constructor/destructor calls at A, B, C, D, E, F strictly necessary in light of the fact that the construction is trivial, and performs no actual initialisation. If so, please indicate which part of the standard contradicts or clarifies object.lifetime 2.2 in this regard.
#include <memory>
// a POD with trivial constructor
struct Foo
{
int x;
};
struct destroy1
{
void operator()(Foo* p)
{
auto memory = std::unique_ptr<unsigned char[]>(reinterpret_cast<unsigned char*>(p));
p->~Foo(); // A
}
};
std::unique_ptr<Foo, destroy1> create1()
{
auto p = std::make_unique<unsigned char[]>(sizeof(Foo));
auto pCandidate = reinterpret_cast<Foo*>(p.get());
new (pCandidate) Foo(); // B
return std::unique_ptr<Foo, destroy1>(reinterpret_cast<Foo*>(p.release()),
destroy1());
}
struct call_free
{
void operator()(void *p) const { std::free(p); }
};
using malloc_ptr = std::unique_ptr<unsigned char, call_free>;
struct destroy2
{
void operator()(Foo *pfoo) const {
auto memory = malloc_ptr(reinterpret_cast<unsigned char*>(pfoo));
pfoo->~Foo(); // C
}
};
std::unique_ptr<Foo, destroy2> create2()
{
auto p = malloc_ptr(reinterpret_cast<unsigned char*>(std::malloc(sizeof(Foo))));
auto pCandidate = reinterpret_cast<Foo*>(p.get());
new (pCandidate) Foo(); // D
return std::unique_ptr<Foo, destroy2>(reinterpret_cast<Foo*>(p.release()),
destroy2());
}
struct nodelete {
void operator()(Foo * p) {
p->~Foo(); // E
}
};
std::shared_ptr<Foo> provide()
{
alignas(Foo) static unsigned char storage[sizeof(Foo)];
auto make = [] {
auto p = reinterpret_cast<Foo*>(storage);
new (p) Foo (); // F
return std::shared_ptr<Foo>(p, nodelete());
};
static std::shared_ptr<Foo> pCandidate = make();
return pCandidate;
}
int main()
{
auto foo1 = create1();
auto foo2 = create2();
auto foo3 = provide();
foo1->x = 1;
foo2->x = 2;
foo3->x = 3;
}