#pragma once #include "asl/assert.hpp" #include "asl/meta.hpp" #include "asl/maybe_uninit.hpp" namespace asl { struct nullopt_t {}; static constexpr nullopt_t nullopt{}; // @Todo(option) Niche // @Todo(option) Reference // @Todo(option) Function // @Todo(option) Arrays template class option { maybe_uninit m_payload; bool m_has_value = false; template constexpr void construct(Args&&... args) & { ASL_ASSERT(!m_has_value); m_payload.init_unsafe(ASL_FWD(args)...); m_has_value = true; } template constexpr void assign(Arg&& arg) & { ASL_ASSERT(m_has_value); m_payload.as_init_unsafe() = ASL_FWD(arg); } public: constexpr option() = default; constexpr option(nullopt_t) {} // NOLINT(*-explicit-conversions) constexpr option(const option& other) requires copy_constructible { if (other.m_has_value) { construct(other.m_payload.as_init_unsafe()); } } constexpr option(option&& other) requires move_constructible { if (other.m_has_value) { construct(ASL_MOVE(other.m_payload.as_init_unsafe())); } } constexpr option& operator=(const option& other) & requires copy_assignable && copy_constructible { if (&other == this) { return *this; } if (other.m_has_value) { if (m_has_value) { assign(other.m_payload.as_init_unsafe()); } else { construct(other.m_payload.as_init_unsafe()); } } else if (m_has_value) { reset(); } return *this; } constexpr option& operator=(option&& other) & requires move_assignable && move_constructible { if (&other == this) { return *this; } if (other.m_has_value) { if (m_has_value) { assign(ASL_MOVE(other.m_payload.as_init_unsafe())); } else { construct(ASL_MOVE(other.m_payload.as_init_unsafe())); } } else if (m_has_value) { reset(); } return *this; } constexpr ~option() = default; constexpr ~option() requires (!trivially_destructible) { reset(); } constexpr void reset() { if (m_has_value) { m_payload.uninit_unsafe(); m_has_value = false; } } }; } // namespace asl