#include namespace detail { // Utilize max value of std::underlying_type_t as uninitialized value for enums template constexpr auto make_unitialized() -> std::enable_if_t, T > { return static_cast(std::numeric_limits>::max()); } // Utilize max value of T as uninitialized value template constexpr auto make_unitialized() -> std::enable_if_t, T > { return std::numeric_limits::max(); } } template class optional_number { public: using value_type = T; constexpr static auto k_uninitialized = detail::make_unitialized(); // Constructors and assignment constexpr optional_number() noexcept : value_{ k_uninitialized } {} constexpr optional_number(const optional_number& iother) noexcept : value_{ iother.value_ } {} constexpr optional_number(optional_number&& iother) noexcept : value_{ iother.value_ } {} constexpr auto& operator=(const optional_number& iother) noexcept { value_ = iother.value_; return *this; } constexpr auto& operator=(optional_number&& iother) noexcept { value_ = iother.value_; return *this; } // Comparisson constexpr auto operator==(const optional_number& iother) const noexcept { return value_ == iother.value_; } constexpr auto operator!=(const optional_number& iother) const noexcept { return value_ != iother.value_; } constexpr auto operator<(const optional_number& iother) const noexcept { if (!iother.has_value()) return false; else if (!has_value()) return true; else return value_ < iother.value_; } // Assignment from braces auto operator=(const std::initializer_list& ivalues) noexcept -> optional_number& { ASSERT(ivalues.size() <= 1); ASSERT(ivalues.size() == 0 || *ivalues.begin() != k_uninitialized); value_ = ivalues.size() > 0 ? (*ivalues.begin()) : k_uninitialized; return *this; } // Initialization from value_type optional_number(const value_type& ivalue) noexcept : value_{ ivalue } { ASSERT(ivalue != k_uninitialized); } auto& operator=(const value_type& ivalue) noexcept { ASSERT(ivalue != k_uninitialized); value_ = ivalue; return *this; } // Observers auto& get() const { ASSERT(is_initialized()); return value_; } auto& value() const { ASSERT(is_initialized()); return value_; } auto& operator*() const { return get(); } constexpr auto is_initialized() const noexcept { return value_ != k_uninitialized; } constexpr auto has_value() const noexcept { return value_ != k_uninitialized; } constexpr auto value_or(const T& ivalue) const noexcept { return is_initialized() ? value_ : ivalue; } // Mutators constexpr auto reset() noexcept { value_ = k_uninitialized; } auto swap(optional_number& other) noexcept { std::swap(value_, other.value_); } auto emplace(const value_type& ivalue) { ASSERT(ivalue != k_uninitialized); value_ = ivalue; } private: value_type value_ = k_uninitialized; };