Is it allowed by the C++ Standard to use storage provided by an object beyond its destruction but before its deallocation?
It is clear that storage can be reused after destroying a regular object. But in the case below, object B
provides storage for an object defined as template parameter T
, and constructs that object in the provided storage during its own construction.
Further, the object in the provided storage is not destroyed when the B
object is destroyed, and the lifetime of the array which is used to provide storage does not end as it has a trivial destructor without effect, which leads to the question if the lifetime of the object of type A
ends with B
or not.
#include <cstddef>
#include <iostream>
#include <memory>
#include <new>
struct A
{
int i = 23;
};
template<class T>
struct B
{
B()
{
// construct object T in the provided storage
new (&c) T;
}
// return a reference to the object in the provided storage
T & t()
{
return *std::launder(reinterpret_cast<T *>(&c));
}
// reserved storage
alignas(T) std::byte c[sizeof(T)];
};
int
main()
{
using Alloc = std::allocator<B<A>>;
using AllocTraits = std::allocator_traits<Alloc>;
Alloc alloc;
// storage duration starts
B<A> * b = AllocTraits::allocate(alloc, 1u);
// construction of both B and A
AllocTraits::construct(alloc, b);
// Get a reference to A
A & a = b->t();
std::cout << "At this point A is alive. a.i = " << a.i << std::endl;
a.i = 42;
// object of type B is destroyed, but A is not
AllocTraits::destroy(alloc, b);
// Is it undefined behaviour to read from 'a'?
std::cout << "Is A still alive? a.i = " << a.i << std::endl;
// A is destroyed
a.~A();
// storage duration ends
AllocTraits::deallocate(alloc, b, 1u);
return 0;
}
Q: Why would anyone do that?
A: It would allow to implement an intrusive control block for a weak pointer without additional overhead as done in https://github.com/john-plate/pntr.