Make optional trivially destructible if the stored type is

This commit is contained in:
Chris Robinson
2021-01-02 13:17:16 -08:00
parent d6495b35df
commit 1f51de9983
+70 -43
View File
@@ -9,27 +9,59 @@
namespace al { namespace al {
#define REQUIRES(...) bool rt_=true, std::enable_if_t<rt_ && (__VA_ARGS__),bool> = true
struct nullopt_t { }; struct nullopt_t { };
struct in_place_t { }; struct in_place_t { };
constexpr nullopt_t nullopt{}; constexpr nullopt_t nullopt{};
constexpr in_place_t in_place{}; constexpr in_place_t in_place{};
template<typename T, bool = std::is_trivially_destructible<T>::value>
struct optional_storage;
template<typename T> template<typename T>
class optional { struct optional_storage<T, true> {
bool mHasValue{false}; bool mHasValue{false};
union { union {
char mDummy[sizeof(T)]{}; char mDummy;
T mValue; T mValue;
}; };
optional_storage() { }
template<typename ...Args> template<typename ...Args>
void DoConstruct(Args&& ...args) explicit optional_storage(in_place_t, Args&& ...args)
: mHasValue{true}, mValue{std::forward<Args>(args)...}
{ }
~optional_storage() = default;
};
template<typename T>
struct optional_storage<T, false> {
bool mHasValue{false};
union {
char mDummy;
T mValue;
};
optional_storage() { }
template<typename ...Args>
explicit optional_storage(in_place_t, Args&& ...args)
: mHasValue{true}, mValue{std::forward<Args>(args)...}
{ }
~optional_storage() { if(mHasValue) al::destroy_at(std::addressof(mValue)); }
};
template<typename T>
class optional {
using storage_t = optional_storage<T>;
storage_t mStore;
template<typename... Args>
void doConstruct(Args&& ...args)
{ {
::new (std::addressof(mValue)) T{std::forward<Args>(args)...}; ::new(std::addressof(mStore.mValue)) T{std::forward<Args>(args)...};
mHasValue = true; mStore.mHasValue = true;
} }
public: public:
@@ -37,15 +69,13 @@ public:
optional() noexcept = default; optional() noexcept = default;
optional(nullopt_t) noexcept { } optional(nullopt_t) noexcept { }
template<REQUIRES(std::is_copy_constructible<T>::value)> optional(const optional &rhs) { if(rhs) doConstruct(*rhs); }
optional(const optional &rhs) { if(rhs) DoConstruct(*rhs); } optional(optional&& rhs) { if(rhs) doConstruct(std::move(*rhs)); }
template<REQUIRES(std::is_move_constructible<T>::value)> template<typename ...Args>
optional(optional&& rhs) { if(rhs) DoConstruct(std::move(*rhs)); } explicit optional(in_place_t, Args&& ...args)
template<typename... Args, REQUIRES(std::is_constructible<T, Args...>::value)> : mStore{al::in_place, std::forward<Args>(args)...}
explicit optional(in_place_t, Args&& ...args) : mHasValue{true}
, mValue{std::forward<Args>(args)...}
{ } { }
~optional() { if(mHasValue) al::destroy_at(std::addressof(mValue)); } ~optional() = default;
optional& operator=(nullopt_t) noexcept { reset(); return *this; } optional& operator=(nullopt_t) noexcept { reset(); return *this; }
std::enable_if_t<std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value, std::enable_if_t<std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value,
@@ -54,9 +84,9 @@ public:
if(!rhs) if(!rhs)
reset(); reset();
else if(*this) else if(*this)
mValue = *rhs; mStore.mValue = *rhs;
else else
DoConstruct(*rhs); doConstruct(*rhs);
return *this; return *this;
} }
std::enable_if_t<std::is_move_constructible<T>::value && std::is_move_assignable<T>::value, std::enable_if_t<std::is_move_constructible<T>::value && std::is_move_assignable<T>::value,
@@ -65,40 +95,39 @@ public:
if(!rhs) if(!rhs)
reset(); reset();
else if(*this) else if(*this)
mValue = std::move(*rhs); mStore.mValue = std::move(*rhs);
else else
DoConstruct(std::move(*rhs)); doConstruct(std::move(*rhs));
return *this; return *this;
} }
template<typename U=T> template<typename U=T>
std::enable_if_t<std::is_constructible<T, U>::value && std::enable_if_t<std::is_constructible<T, U>::value
std::is_assignable<T&, U>::value && && std::is_assignable<T&, U>::value
!std::is_same<typename std::decay<U>::type, optional<T>>::value && && !std::is_same<std::decay_t<U>, optional<T>>::value
(!std::is_same<typename std::decay<U>::type, T>::value || && (!std::is_same<std::decay_t<U>, T>::value || !std::is_scalar<U>::value),
!std::is_scalar<U>::value),
optional&> operator=(U&& rhs) optional&> operator=(U&& rhs)
{ {
if(*this) if(*this)
mValue = std::forward<U>(rhs); mStore.mValue = std::forward<U>(rhs);
else else
DoConstruct(std::forward<U>(rhs)); doConstruct(std::forward<U>(rhs));
return *this; return *this;
} }
const T* operator->() const { return std::addressof(mValue); } const T* operator->() const { return std::addressof(mStore.mValue); }
T* operator->() { return std::addressof(mValue); } T* operator->() { return std::addressof(mStore.mValue); }
const T& operator*() const& { return mValue; } const T& operator*() const& { return this->mValue; }
T& operator*() & { return mValue; } T& operator*() & { return mStore.mValue; }
const T&& operator*() const&& { return std::move(mValue); } const T&& operator*() const&& { return std::move(mStore.mValue); }
T&& operator*() && { return std::move(mValue); } T&& operator*() && { return std::move(mStore.mValue); }
operator bool() const noexcept { return mHasValue; } operator bool() const noexcept { return mStore.mHasValue; }
bool has_value() const noexcept { return mHasValue; } bool has_value() const noexcept { return mStore.mHasValue; }
T& value() & { return mValue; } T& value() & { return mStore.mValue; }
const T& value() const& { return mValue; } const T& value() const& { return mStore.mValue; }
T&& value() && { return std::move(mValue); } T&& value() && { return std::move(mStore.mValue); }
const T&& value() const&& { return std::move(mValue); } const T&& value() const&& { return std::move(mStore.mValue); }
template<typename U> template<typename U>
T value_or(U&& defval) const& T value_or(U&& defval) const&
@@ -109,9 +138,9 @@ public:
void reset() noexcept void reset() noexcept
{ {
if(mHasValue) if(mStore.mHasValue)
al::destroy_at(std::addressof(mValue)); al::destroy_at(std::addressof(mStore.mValue));
mHasValue = false; mStore.mHasValue = false;
} }
}; };
@@ -127,8 +156,6 @@ template<typename T, typename U, typename... Args>
inline optional<T> make_optional(std::initializer_list<U> il, Args&& ...args) inline optional<T> make_optional(std::initializer_list<U> il, Args&& ...args)
{ return optional<T>{in_place, il, std::forward<Args>(args)...}; } { return optional<T>{in_place, il, std::forward<Args>(args)...}; }
#undef REQUIRES
} // namespace al } // namespace al
#endif /* AL_OPTIONAL_H */ #endif /* AL_OPTIONAL_H */