blob: 1fb3f312f75b2885a85856382dacc193b25d0660 (
plain) (
tree)
|
|
#pragma once
#include <memory>
#include <type_traits>
#include <initializer_list>
namespace NMaybe {
struct TInPlace {};
template <class T, bool = std::is_trivially_destructible<T>::value>
struct TStorageBase {
constexpr TStorageBase() noexcept
: NullState_('\0')
{
}
template <class... Args>
constexpr TStorageBase(TInPlace, Args&&... args)
: Data_(std::forward<Args>(args)...)
, Defined_(true)
{
}
constexpr TStorageBase(TStorageBase&&) = default;
constexpr TStorageBase(const TStorageBase&) = default;
~TStorageBase() = default;
TStorageBase& operator=(const TStorageBase&) = default;
TStorageBase& operator=(TStorageBase&&) = default;
union {
char NullState_;
T Data_;
};
bool Defined_ = false;
};
template <class T>
struct TStorageBase<T, false> {
constexpr TStorageBase() noexcept
: NullState_('\0')
{
}
template <class... Args>
constexpr TStorageBase(TInPlace, Args&&... args)
: Data_(std::forward<Args>(args)...)
, Defined_(true)
{
}
constexpr TStorageBase(TStorageBase&&) = default;
constexpr TStorageBase(const TStorageBase&) = default;
~TStorageBase() {
if (this->Defined_) {
this->Data_.~T();
}
}
TStorageBase& operator=(const TStorageBase&) = default;
TStorageBase& operator=(TStorageBase&&) = default;
union {
char NullState_;
T Data_;
};
bool Defined_ = false;
};
// -------------------- COPY CONSTRUCT --------------------
template <class T, bool = std::is_trivially_copy_constructible<T>::value>
struct TCopyBase: TStorageBase<T> {
using TStorageBase<T>::TStorageBase;
};
template <class T>
struct TCopyBase<T, false>: TStorageBase<T> {
using TStorageBase<T>::TStorageBase;
constexpr TCopyBase() = default;
constexpr TCopyBase(const TCopyBase& rhs) {
if (rhs.Defined_) {
new (std::addressof(this->Data_)) T(rhs.Data_);
this->Defined_ = true;
}
}
constexpr TCopyBase(TCopyBase&&) = default;
TCopyBase& operator=(const TCopyBase&) = default;
TCopyBase& operator=(TCopyBase&&) = default;
};
// -------------------- MOVE CONSTRUCT --------------------
template <class T, bool = std::is_trivially_move_constructible<T>::value>
struct TMoveBase: TCopyBase<T> {
using TCopyBase<T>::TCopyBase;
};
template <class T>
struct TMoveBase<T, false>: TCopyBase<T> {
using TCopyBase<T>::TCopyBase;
constexpr TMoveBase() noexcept = default;
constexpr TMoveBase(const TMoveBase&) = default;
constexpr TMoveBase(TMoveBase&& rhs) noexcept(std::is_nothrow_move_constructible<T>::value) {
if (rhs.Defined_) {
new (std::addressof(this->Data_)) T(std::move(rhs.Data_));
this->Defined_ = true;
}
}
TMoveBase& operator=(const TMoveBase&) = default;
TMoveBase& operator=(TMoveBase&&) = default;
};
// -------------------- COPY ASSIGN --------------------
template <class T, bool = std::is_trivially_copy_assignable<T>::value>
struct TCopyAssignBase: TMoveBase<T> {
using TMoveBase<T>::TMoveBase;
};
template <class T>
struct TCopyAssignBase<T, false>: TMoveBase<T> {
using TMoveBase<T>::TMoveBase;
constexpr TCopyAssignBase() noexcept = default;
constexpr TCopyAssignBase(const TCopyAssignBase&) = default;
constexpr TCopyAssignBase(TCopyAssignBase&&) = default;
TCopyAssignBase& operator=(const TCopyAssignBase& rhs) {
if (this->Defined_) {
if (rhs.Defined_) {
this->Data_ = rhs.Data_;
} else {
this->Data_.~T();
this->Defined_ = false;
}
} else if (rhs.Defined_) {
new (std::addressof(this->Data_)) T(rhs.Data_);
this->Defined_ = true;
}
return *this;
}
TCopyAssignBase& operator=(TCopyAssignBase&&) = default;
};
// -------------------- MOVE ASSIGN --------------------
template <class T, bool = std::is_trivially_move_assignable<T>::value>
struct TMoveAssignBase: TCopyAssignBase<T> {
using TCopyAssignBase<T>::TCopyAssignBase;
};
template <class T>
struct TMoveAssignBase<T, false>: TCopyAssignBase<T> {
using TCopyAssignBase<T>::TCopyAssignBase;
constexpr TMoveAssignBase() noexcept = default;
constexpr TMoveAssignBase(const TMoveAssignBase&) = default;
constexpr TMoveAssignBase(TMoveAssignBase&&) = default;
TMoveAssignBase& operator=(const TMoveAssignBase&) = default;
TMoveAssignBase& operator=(TMoveAssignBase&& rhs) noexcept(
std::is_nothrow_move_assignable<T>::value &&
std::is_nothrow_move_constructible<T>::value)
{
if (this->Defined_) {
if (rhs.Defined_) {
this->Data_ = std::move(rhs.Data_);
} else {
this->Data_.~T();
this->Defined_ = false;
}
} else if (rhs.Defined_) {
new (std::addressof(this->Data_)) T(std::move(rhs.Data_));
this->Defined_ = true;
}
return *this;
}
};
} // namespace NMaybe
template <class T>
using TMaybeBase = NMaybe::TMoveAssignBase<T>;
|