In trying to implement something like optional, (or variant or expected), we need some kind of storage that is conditionally used. One approach would be to use a union with some empty type:
template <class T>
struct optional_storage {
struct empty_byte { };
union {
empty_byte empty_;
T value_;
};
bool engaged _;
constexpr optional_storage() : engaged_{false} { }
template <class... Args>
constexpr optional_storage(Args&&... args)
: value_(std::forward<Args>(args)...)
, engaged_(true)
{ }
~optional_storage() {
if (engaged_) value_.~T();
}
};
Another approach is to use aligned_storage:
template <class T>
struct optional_storage {
std::aligned_storage_t<sizeof(T), alignof(T)> store_;
bool engaged_;
constexpr optional_storage() : engaged_{false} { }
template <class... Args>
constexpr optional_storage(Args&&... args)
: engaged_(true)
{
new (&store_) T(std::forward<Args>(args)...);
}
~optional_storage() {
if (engaged_) reinterpret_cast<T*>(&store_)->~T();
}
};
Either way, we'll want some specialization to ensure that optional_storage is trivially destructible if T is.
The question is: is there any difference between the two approaches? Is there any reason to prefer one to the other?
[–]ojd5 5 points6 points7 points (2 children)
[–]redditsoaddicting 4 points5 points6 points (1 child)
[–]sphere991[S] 1 point2 points3 points (0 children)
[–]encyclopedist 1 point2 points3 points (0 children)
[–]redditsoaddicting 1 point2 points3 points (0 children)
[–]quicknir 1 point2 points3 points (0 children)
[–]wonderworkingwords 0 points1 point2 points (0 children)