#pragma once #include #include #include #include #include #include #include #include // NOLINTBEGIN(modernize-use-constraints) namespace stdx { inline namespace v1 { template struct tombstone_traits { static_assert( stdx::always_false_v, "To use stdx::optional you must specialize stdx::tombstone_traits"); }; template struct tombstone_traits>> { constexpr auto operator()() const { return std::numeric_limits::infinity(); } }; template struct tombstone_traits>> { constexpr auto operator()() const { return nullptr; } }; template struct tombstone_value { constexpr auto operator()() const { if constexpr (stdx::is_cx_value_v) { return V(); } else { return V; } } }; template > class optional { static_assert(not std::is_integral_v or not stdx::is_specialization_of_v, "Don't define tombstone traits for plain integral types"); constexpr static inline auto traits = TS{}; T val{traits()}; public: using value_type = T; constexpr optional() = default; constexpr explicit optional(std::nullopt_t) {} template constexpr explicit optional(std::in_place_t, Args &&...args) : val{std::forward(args)...} {} template < typename U = T, typename = std::enable_if_t< std::is_constructible_v and not std::is_same_v, std::in_place_t> and not std::is_same_v, optional>>> constexpr explicit optional(U &&u) : val{std::forward(u)} {} constexpr auto operator=(std::nullopt_t) -> optional & { reset(); return *this; } template < typename U = T, typename = std::enable_if_t< std::is_constructible_v and std::is_assignable_v and not std::is_same_v, optional> and (std::is_scalar_v or not std::is_same_v, T>)>> constexpr auto operator=(U &&u) -> optional & { val = std::forward(u); return *this; } [[nodiscard]] constexpr auto has_value() const noexcept -> bool { return not(val == traits()); } constexpr explicit operator bool() const noexcept { return has_value(); } [[nodiscard]] constexpr auto value() & LIFETIMEBOUND -> value_type & { return val; } [[nodiscard]] constexpr auto value() const & LIFETIMEBOUND -> value_type const & { return val; } [[nodiscard]] constexpr auto value() && LIFETIMEBOUND -> value_type && { return std::move(val); } [[nodiscard]] constexpr auto value() const && LIFETIMEBOUND -> value_type const && { return std::move(val); } [[nodiscard]] constexpr auto operator->() const LIFETIMEBOUND->value_type const * { return std::addressof(val); } [[nodiscard]] constexpr auto operator->() LIFETIMEBOUND->value_type * { return std::addressof(val); } [[nodiscard]] constexpr auto operator*() const & LIFETIMEBOUND->decltype(auto) { return value(); } [[nodiscard]] constexpr auto operator*() & LIFETIMEBOUND->decltype(auto) { return value(); } [[nodiscard]] constexpr auto operator*() const && LIFETIMEBOUND->decltype(auto) { return std::move(*this).value(); } [[nodiscard]] constexpr auto operator*() && LIFETIMEBOUND->decltype(auto) { return std::move(*this).value(); } template [[nodiscard]] constexpr auto value_or(U &&default_value) const & -> value_type { return has_value() ? val : T{std::forward(default_value)}; } template [[nodiscard]] constexpr auto value_or(U &&default_value) && -> value_type { return has_value() ? std::move(val) : T{std::forward(default_value)}; } template constexpr auto emplace(Args &&...args) LIFETIMEBOUND -> value_type & { val.~value_type(); new (std::addressof(val)) value_type(std::forward(args)...); return value(); } constexpr auto reset() { val.~value_type(); new (std::addressof(val)) value_type(traits()); } template constexpr auto transform(F &&f) & { using func_t = stdx::remove_cvref_t; using U = std::invoke_result_t; return *this ? optional{with_result_of{ [&] { return std::forward(f)(val); }}} : optional{}; } template constexpr auto transform(F &&f) const & { using func_t = stdx::remove_cvref_t; using U = std::invoke_result_t; return *this ? optional{with_result_of{ [&] { return std::forward(f)(val); }}} : optional{}; } template constexpr auto transform(F &&f) && { using func_t = stdx::remove_cvref_t; using U = std::invoke_result_t; return *this ? optional{with_result_of{ [&] { return std::forward(f)(std::move(val)); }}} : optional{}; } template constexpr auto transform(F &&f) const && { using func_t = stdx::remove_cvref_t; using U = std::invoke_result_t; return *this ? optional{with_result_of{ [&] { return std::forward(f)(std::move(val)); }}} : optional{}; } template constexpr auto or_else(F &&f) const & -> optional { return *this ? *this : std::forward(f)(); } template constexpr auto or_else(F &&f) && -> optional { return *this ? std::move(*this) : std::forward(f)(); } template constexpr auto and_then(F &&f) & { using func_t = stdx::remove_cvref_t; using U = std::invoke_result_t; return *this ? std::forward(f)(val) : U{}; } template constexpr auto and_then(F &&f) const & { using func_t = stdx::remove_cvref_t; using U = std::invoke_result_t; return *this ? std::forward(f)(val) : U{}; } template constexpr auto and_then(F &&f) && { using func_t = stdx::remove_cvref_t; using U = std::invoke_result_t; return *this ? std::forward(f)(std::move(val)) : U{}; } template constexpr auto and_then(F &&f) const && { using func_t = stdx::remove_cvref_t; using U = std::invoke_result_t; return *this ? std::forward(f)(std::move(val)) : U{}; } private: [[nodiscard]] friend constexpr auto operator==(optional const &lhs, optional const &rhs) -> bool { return lhs.val == rhs.val; } #if __cpp_impl_three_way_comparison < 201907L [[nodiscard]] friend constexpr auto operator!=(optional const &lhs, optional const &rhs) -> bool { return not(lhs == rhs); } #endif [[nodiscard]] friend constexpr auto operator<(optional const &lhs, optional const &rhs) -> bool { return lhs.has_value() and rhs.has_value() ? lhs.val < rhs.val : not lhs.has_value() and rhs.has_value(); } [[nodiscard]] friend constexpr auto operator<=(optional const &lhs, optional const &rhs) -> bool { return not(rhs < lhs); } [[nodiscard]] friend constexpr auto operator>(optional const &lhs, optional const &rhs) -> bool { return rhs < lhs; } [[nodiscard]] friend constexpr auto operator>=(optional const &lhs, optional const &rhs) -> bool { return not(lhs < rhs); } }; template optional(T) -> optional; template , optional>)>> constexpr auto transform(F &&f, Ts &&...ts) { using func_t = stdx::remove_cvref_t; using R = std::invoke_result_t< func_t, forward_like_t::value_type>...>; if ((... and ts.has_value())) { return optional{with_result_of{[&] { return std::forward(f)(std::forward(ts).value()...); }}}; } return optional{}; } } // namespace v1 } // namespace stdx // NOLINTEND(modernize-use-constraints)