I have seen the following pattern several times:
// T is a type, this is at namespace scope
std::aligned_storage_t<sizeof(T), alignof(T)> storage;
T &t = reinterpret_cast<T &>(storage);
This, coupled with adequate namespacing and naming, provides a pleasant interface (t
) to users of the variable, while enabling deferred construction, reinitialization, etc of the actual object on the library side though placement new
and explicit destructor calls. You can see it working here.
Now, std::aligned_storage
is neat and all, but C++17 gave us a new tool in the box for such storage-vs-object lifetime splitting, that is std::optional
.
However, the two ways of accessing the value of an std::optional
(value()
and operator*
) both require a value to actually be there; otherwise value()
will throw std::bad_optional_access
, while operator*
will trigger undefined behaviour (via breaking the requires clause in [optional.observe]§5).
std::optional<T> storage;
T &t = *storage; // Looks okay, mines bitcoin when you're not looking
Is such a usage of std::optional
still possible somehow?
If not, what would be a reason for preventing it?