#pragma once #include #include #include #include namespace BAN { template class Optional { public: constexpr Optional(); constexpr Optional(Optional&&); constexpr Optional(const Optional&); constexpr Optional(const T&); constexpr Optional(T&&); ~Optional(); constexpr Optional& operator=(Optional&&); constexpr Optional& operator=(const Optional&); template constexpr Optional& emplace(Args&&...); constexpr T* operator->(); constexpr const T* operator->() const; constexpr T& operator*(); constexpr const T& operator*() const; constexpr bool has_value() const; constexpr T release_value(); constexpr T& value(); constexpr const T& value() const; constexpr T& value_or(T&); constexpr const T& value_or(const T&) const; constexpr void clear(); private: alignas(T) uint8_t m_storage[sizeof(T)] {}; bool m_has_value { false }; }; template constexpr Optional::Optional() : m_has_value(false) {} template constexpr Optional::Optional(Optional&& other) : m_has_value(other.has_value()) { if (other.has_value()) new (m_storage) T(move(other.release_value())); } template constexpr Optional::Optional(const Optional& other) : m_has_value(other.has_value()) { if (other.has_value()) new (m_storage) T(other.value()); } template constexpr Optional::Optional(const T& value) : m_has_value(true) { new (m_storage) T(value); } template constexpr Optional::Optional(T&& value) : m_has_value(true) { new (m_storage) T(move(value)); } template Optional::~Optional() { clear(); } template constexpr Optional& Optional::operator=(Optional&& other) { clear(); m_has_value = other.has_value(); if (other.has_value()) new (m_storage) T(move(other.release_value())); return *this; } template constexpr Optional& Optional::operator=(const Optional& other) { clear(); m_has_value = other.has_value(); if (other.has_value()) new (m_storage) T(other.value()); return *this; } template template constexpr Optional& Optional::emplace(Args&&... args) { clear(); m_has_value = true; new (m_storage) T(forward(args)...); return *this; } template constexpr T* Optional::operator->() { ASSERT(has_value()); return &value(); } template constexpr const T* Optional::operator->() const { ASSERT(has_value()); return &value(); } template constexpr T& Optional::operator*() { ASSERT(has_value()); return value(); } template constexpr const T& Optional::operator*() const { ASSERT(has_value()); return value(); } template constexpr bool Optional::has_value() const { return m_has_value; } template constexpr T Optional::release_value() { ASSERT(has_value()); T released_value = move(value()); value().~T(); m_has_value = false; return move(released_value); } template constexpr T& Optional::value() { ASSERT(has_value()); return (T&)m_storage; } template constexpr const T& Optional::value() const { ASSERT(has_value()); return (const T&)m_storage; } template constexpr T& Optional::value_or(T& empty) { if (!has_value()) return empty; return (T&)m_storage; } template constexpr const T& Optional::value_or(const T& empty) const { if (!has_value()) return empty; return (const T&)m_storage; } template constexpr void Optional::clear() { if (m_has_value) value().~T(); m_has_value = false; } }