Make optional trivially destructible if the stored type is
This commit is contained in:
+70
-43
@@ -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>
|
||||||
|
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>
|
template<typename... Args>
|
||||||
void DoConstruct(Args&& ...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 */
|
||||||
|
|||||||
Reference in New Issue
Block a user