/// \file // Range v3 library // // Copyright Casey Carter 2017 // // Use, modification and distribution is subject to the // Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // Project home: https://github.com/ericniebler/range-v3 // #ifndef RANGES_V3_UTILITY_OPTIONAL_HPP #define RANGES_V3_UTILITY_OPTIONAL_HPP #include #include #include #include #include #include #include #include #include #include namespace ranges { template struct optional; struct bad_optional_access : std::exception { virtual const char * what() const noexcept override { return "bad optional access"; } }; struct nullopt_t { struct tag {}; explicit constexpr nullopt_t(tag) noexcept {} }; #if RANGES_CXX_INLINE_VARIABLES >= RANGES_CXX_INLINE_VARIABLES_17 inline constexpr nullopt_t nullopt{nullopt_t::tag{}}; #else /// \cond namespace detail { template struct nullopt_holder { static constexpr nullopt_t nullopt{nullopt_t::tag{}}; }; template constexpr nullopt_t nullopt_holder::nullopt; } // namespace detail /// \endcond namespace { constexpr auto & nullopt = detail::nullopt_holder::nullopt; } #endif /// \cond namespace detail { template [[noreturn]] bool throw_bad_optional_access() { throw bad_optional_access{}; } namespace optional_adl { template::value> struct optional_storage { union { char dummy_; meta::_t> data_; }; bool engaged_; constexpr optional_storage() noexcept : optional_storage( tag{}, meta::strict_and, detail::is_trivially_copyable>{}) {} CPP_template(typename... Args)( // requires constructible_from) // constexpr explicit optional_storage(in_place_t, Args &&... args) // noexcept(std::is_nothrow_constructible::value) : data_(static_cast(args)...) , engaged_{true} {} constexpr void reset() noexcept { engaged_ = false; } private: struct tag {}; constexpr optional_storage(tag, std::false_type) noexcept : dummy_{} , engaged_{false} {} constexpr optional_storage(tag, std::true_type) noexcept : data_{} , engaged_{false} {} }; template struct optional_storage { union { char dummy_; meta::_t> data_; }; bool engaged_; ~optional_storage() { reset(); } constexpr optional_storage() noexcept : dummy_{} , engaged_{false} {} CPP_template(typename... Args)( // requires constructible_from) // constexpr explicit optional_storage(in_place_t, Args &&... args) // noexcept(std::is_nothrow_constructible::value) : data_(static_cast(args)...) , engaged_{true} {} optional_storage(optional_storage const &) = default; optional_storage(optional_storage &&) = default; optional_storage & operator=(optional_storage const &) = default; optional_storage & operator=(optional_storage &&) = default; void reset() noexcept { if(engaged_) { data_.~T(); engaged_ = false; } } }; template struct optional_base : private optional_storage { using optional_storage::optional_storage; using optional_storage::reset; constexpr bool has_value() const noexcept { return engaged_; } constexpr T & operator*() & noexcept { return RANGES_EXPECT(engaged_), data_; } constexpr T const & operator*() const & noexcept { return RANGES_EXPECT(engaged_), data_; } constexpr T && operator*() && noexcept { return RANGES_EXPECT(engaged_), detail::move(data_); } constexpr T const && operator*() const && noexcept { return RANGES_EXPECT(engaged_), detail::move(data_); } constexpr T * operator->() noexcept { return RANGES_EXPECT(engaged_), detail::addressof(data_); } constexpr T const * operator->() const noexcept { return RANGES_EXPECT(engaged_), detail::addressof(data_); } CPP_member constexpr auto swap(optional_base & that) noexcept( std::is_nothrow_move_constructible::value && is_nothrow_swappable::value) -> CPP_ret(void)( // requires move_constructible && swappable) { constexpr bool can_swap_trivially = !::concepts::adl_swap_detail::is_adl_swappable_v && detail::is_trivially_move_constructible::value && detail::is_trivially_move_assignable::value; swap_(meta::bool_{}, that); } protected: template auto construct_from(Args &&... args) noexcept( std::is_nothrow_constructible::value) -> CPP_ret(T &)( // requires constructible_from) { RANGES_EXPECT(!engaged_); auto const address = static_cast(std::addressof(data_)); ::new(address) T(static_cast(args)...); engaged_ = true; return data_; } template constexpr void assign_from(U && that) noexcept( std::is_nothrow_constructible(that))>:: value && std::is_nothrow_assignable< T &, decltype(*static_cast(that))>::value) { if(!that.has_value()) reset(); else if(engaged_) data_ = *static_cast(that); else { auto const address = static_cast(detail::addressof(data_)); ::new(address) T(*static_cast(that)); engaged_ = true; } } private: constexpr void swap_(std::true_type, optional_base & that) noexcept { ranges::swap(static_cast &>(*this), static_cast &>(that)); } constexpr void swap_(std::false_type, optional_base & that) noexcept( std::is_nothrow_move_constructible::value && is_nothrow_swappable::value) { if(that.engaged_ == engaged_) { if(engaged_) ranges::swap(data_, that.data_); } else { auto & src = engaged_ ? *this : that; auto & dst = engaged_ ? that : *this; dst.construct_from(detail::move(src.data_)); src.reset(); } } using optional_storage::engaged_; using optional_storage::data_; }; template struct optional_base { optional_base() = default; template constexpr explicit CPP_ctor(optional_base)(in_place_t, Arg && arg)( // noexcept(true) // requires constructible_from) : ptr_(detail::addressof(arg)) {} constexpr bool has_value() const noexcept { return ptr_; } constexpr T & operator*() const noexcept { return RANGES_EXPECT(ptr_), *ptr_; } constexpr T * operator->() const noexcept { return RANGES_EXPECT(ptr_), ptr_; } constexpr void reset() noexcept { ptr_ = nullptr; } CPP_member constexpr auto swap(optional_base & that) noexcept( is_nothrow_swappable::value) -> CPP_ret(void)( // requires swappable) { if(ptr_ && that.ptr_) ranges::swap(*ptr_, *that.ptr_); else ranges::swap(ptr_, that.ptr_); } protected: template constexpr auto construct_from(U && ref) noexcept -> CPP_ret(T &)( // requires convertible_to) { RANGES_EXPECT(!ptr_); ptr_ = detail::addressof(ref); return *ptr_; } template constexpr void assign_from(U && that) { if(ptr_ && that.ptr_) *ptr_ = *that.ptr_; else ptr_ = that.ptr_; } private: T * ptr_ = nullptr; }; template struct optional_copy : optional_base { optional_copy() = default; optional_copy(optional_copy const & that) noexcept( std::is_nothrow_copy_constructible::value) { if(that.has_value()) this->construct_from(*that); } optional_copy(optional_copy &&) = default; optional_copy & operator=(optional_copy const &) = default; optional_copy & operator=(optional_copy &&) = default; using optional_base::optional_base; }; template using copy_construct_layer = meta::if_c::value && !detail::is_trivially_copy_constructible::value, optional_copy, optional_base>; template struct optional_move : copy_construct_layer { optional_move() = default; optional_move(optional_move const &) = default; optional_move(optional_move && that) noexcept( std::is_nothrow_move_constructible::value) { if(that.has_value()) this->construct_from(std::move(*that)); } optional_move & operator=(optional_move const &) = default; optional_move & operator=(optional_move &&) = default; using copy_construct_layer::copy_construct_layer; }; template using move_construct_layer = meta::if_c::value && !detail::is_trivially_move_constructible::value, optional_move, copy_construct_layer>; template struct optional_copy_assign : move_construct_layer { optional_copy_assign() = default; optional_copy_assign(optional_copy_assign const &) = default; optional_copy_assign(optional_copy_assign &&) = default; optional_copy_assign & operator=(optional_copy_assign const & that) // noexcept(std::is_nothrow_copy_constructible::value && std::is_nothrow_copy_assignable::value) { this->assign_from(that); return *this; } optional_copy_assign & operator=(optional_copy_assign &&) = default; using move_construct_layer::move_construct_layer; }; template struct deleted_copy_assign : move_construct_layer { deleted_copy_assign() = default; deleted_copy_assign(deleted_copy_assign const &) = default; deleted_copy_assign(deleted_copy_assign &&) = default; deleted_copy_assign & operator=(deleted_copy_assign const &) = delete; deleted_copy_assign & operator=(deleted_copy_assign &&) = default; using move_construct_layer::move_construct_layer; }; template using copy_assign_layer = meta::if_c< std::is_copy_constructible::value && std::is_copy_assignable::value, meta::if_c::value || !(detail::is_trivially_copy_constructible::value && detail::is_trivially_copy_assignable::value), optional_copy_assign, move_construct_layer>, deleted_copy_assign>; template struct optional_move_assign : copy_assign_layer { optional_move_assign() = default; optional_move_assign(optional_move_assign const &) = default; optional_move_assign(optional_move_assign &&) = default; optional_move_assign & operator=(optional_move_assign const &) = default; optional_move_assign & operator=(optional_move_assign && that) noexcept( std::is_nothrow_move_constructible::value && std::is_nothrow_move_assignable::value) { this->assign_from(std::move(that)); return *this; } using copy_assign_layer::copy_assign_layer; }; template struct deleted_move_assign : copy_assign_layer { deleted_move_assign() = default; deleted_move_assign(deleted_move_assign const &) = default; deleted_move_assign(deleted_move_assign &&) = default; deleted_move_assign & operator=(deleted_move_assign const &) = default; deleted_move_assign & operator=(deleted_move_assign &&) = delete; using copy_assign_layer::copy_assign_layer; }; template using move_assign_layer = meta::if_c< std::is_move_constructible::value && std::is_move_assignable::value, meta::if_c::value || !(detail::is_trivially_move_constructible::value && detail::is_trivially_move_assignable::value), optional_move_assign, copy_assign_layer>, deleted_move_assign>; } // namespace optional_adl } // namespace detail /// \endcond // clang-format off CPP_def ( template(typename U, typename T) concept optional_should_convert, requires(int)(void()) && !( constructible_from & > || constructible_from && > || constructible_from const & > || constructible_from const &&> || convertible_to &, T> || convertible_to &&, T> || convertible_to const &, T> || convertible_to const &&, T> ) ); CPP_def ( template(typename U, typename T) concept optional_should_convert_assign, optional_should_convert && !(assignable_from &> || assignable_from &&> || assignable_from const &> || assignable_from const &&>) ); // clang-format on template struct optional : detail::optional_adl::move_assign_layer { private: using base_t = detail::optional_adl::move_assign_layer; public: CPP_assert(destructible); static_assert(std::is_object::value || std::is_lvalue_reference::value, ""); static_assert((bool)!same_as>, ""); static_assert((bool)!same_as>, ""); using value_type = meta::_t>; constexpr optional() noexcept {} constexpr optional(nullopt_t) noexcept : optional{} {} optional(optional const &) = default; optional(optional &&) = default; using base_t::base_t; CPP_template(typename E, typename... Args)( // requires constructible_from &, Args...>) // constexpr explicit optional(in_place_t, std::initializer_list il, Args &&... args) // noexcept(std::is_nothrow_constructible &, Args...>::value) : base_t(in_place, il, static_cast(args)...) {} template constexpr CPP_ctor(optional)(U && v)( // requires(!defer::same_as, in_place_t>) && (!defer::same_as, optional>)&&defer::constructible_from && defer::convertible_to) : base_t(in_place, static_cast(v)) {} template explicit constexpr CPP_ctor(optional)(U && v)( // requires(!defer::same_as, in_place_t>) && (!defer::same_as, optional>)&&defer::constructible_from && (!defer::convertible_to)) : base_t(in_place, static_cast(v)) {} template CPP_ctor(optional)(optional const & that)( // requires optional_should_convert && constructible_from && convertible_to) { if(that.has_value()) base_t::construct_from(*that); } template explicit CPP_ctor(optional)(optional const & that)( // requires optional_should_convert && constructible_from && (!convertible_to)) { if(that.has_value()) base_t::construct_from(*that); } template CPP_ctor(optional)(optional && that)( // requires optional_should_convert && constructible_from && convertible_to) { if(that.has_value()) base_t::construct_from(detail::move(*that)); } template explicit CPP_ctor(optional)(optional && that)( // requires optional_should_convert && constructible_from && (!convertible_to)) { if(that.has_value()) base_t::construct_from(detail::move(*that)); } constexpr optional & operator=(nullopt_t) noexcept { reset(); return *this; } optional & operator=(optional const &) = default; optional & operator=(optional &&) = default; template constexpr auto operator=(U && u) noexcept( std::is_nothrow_constructible::value && std::is_nothrow_assignable::value) -> CPP_ret(optional &)( // requires(!defer::same_as>) && (!(defer::satisfies && defer::same_as>)) && defer::constructible_from && defer::assignable_from) { if(has_value()) **this = static_cast(u); else base_t::construct_from(static_cast(u)); return *this; } template constexpr auto operator=(optional const & that) -> CPP_ret(optional &)( // requires optional_should_convert_assign && constructible_from && assignable_from) { base_t::assign_from(that); return *this; } template constexpr auto operator=(optional && that) -> CPP_ret(optional &)( // requires optional_should_convert_assign && constructible_from && assignable_from) { base_t::assign_from(std::move(that)); return *this; } template auto emplace(Args &&... args) noexcept( std::is_nothrow_constructible::value) -> CPP_ret(T &)( // requires constructible_from) { reset(); return base_t::construct_from(static_cast(args)...); } template auto emplace(std::initializer_list il, Args &&... args) noexcept( std::is_nothrow_constructible &, Args...>::value) -> CPP_ret(T &)( // requires constructible_from &, Args &&...>) { reset(); return base_t::construct_from(il, static_cast(args)...); } using base_t::swap; using base_t::operator->; using base_t::operator*; constexpr explicit operator bool() const noexcept { return has_value(); } using base_t::has_value; constexpr T const & value() const & { return (has_value() || detail::throw_bad_optional_access()), **this; } constexpr T & value() & { return (has_value() || detail::throw_bad_optional_access()), **this; } constexpr T const && value() const && { return (has_value() || detail::throw_bad_optional_access()), detail::move(**this); } constexpr T && value() && { return (has_value() || detail::throw_bad_optional_access()), detail::move(**this); } CPP_template(typename U)( // requires copy_constructible && convertible_to) // constexpr T value_or(U && u) const & { return has_value() ? **this : static_cast((U &&) u); } CPP_template(typename U)( // requires move_constructible && convertible_to) // constexpr T value_or(U && u) && { return has_value() ? detail::move(**this) : static_cast((U &&) u); } using base_t::reset; }; /// \cond namespace detail { namespace optional_adl { constexpr bool convert_bool(bool b) noexcept { return b; } // Relational operators [optional.relops] template constexpr auto operator==(optional const & x, optional const & y) // noexcept(noexcept(convert_bool(*x == *y))) -> decltype(convert_bool(*x == *y)) { return x.has_value() == y.has_value() && (!x || convert_bool(*x == *y)); } template constexpr auto operator!=(optional const & x, optional const & y) // noexcept(noexcept(convert_bool(*x != *y))) -> decltype(convert_bool(*x != *y)) { return x.has_value() != y.has_value() || (x && convert_bool(*x != *y)); } template constexpr auto operator<(optional const & x, optional const & y) // noexcept(noexcept(convert_bool(*x < *y))) -> decltype(convert_bool(*x < *y)) { return y && (!x || convert_bool(*x < *y)); } template constexpr auto operator>(optional const & x, optional const & y) // noexcept(noexcept(convert_bool(*x > *y))) -> decltype(convert_bool(*x > *y)) { return x && (!y || convert_bool(*x > *y)); } template constexpr auto operator<=(optional const & x, optional const & y) // noexcept(noexcept(convert_bool(*x <= *y))) -> decltype(convert_bool(*x <= *y)) { return !x || (y && convert_bool(*x <= *y)); } template constexpr auto operator>=(optional const & x, optional const & y) // noexcept(noexcept(convert_bool(*x >= *y))) -> decltype(convert_bool(*x >= *y)) { return !y || (x && convert_bool(*x >= *y)); } // Comparisons with nullopt [optional.nullops] template constexpr bool operator==(optional const & x, nullopt_t) noexcept { return !x; } template constexpr bool operator==(nullopt_t, optional const & x) noexcept { return !x; } template constexpr bool operator!=(optional const & x, nullopt_t) noexcept { return !!x; } template constexpr bool operator!=(nullopt_t, optional const & x) noexcept { return !!x; } template constexpr bool operator<(optional const &, nullopt_t) noexcept { return false; } template constexpr bool operator<(nullopt_t, optional const & x) noexcept { return !!x; } template constexpr bool operator>(optional const & x, nullopt_t) noexcept { return !!x; } template constexpr bool operator>(nullopt_t, optional const &) noexcept { return false; } template constexpr bool operator<=(optional const & x, nullopt_t) noexcept { return !x; } template constexpr bool operator<=(nullopt_t, optional const &) noexcept { return true; } template constexpr bool operator>=(optional const &, nullopt_t) noexcept { return true; } template constexpr bool operator>=(nullopt_t, optional const & x) noexcept { return !x; } // Comparisons with T [optional.comp_with_t] template constexpr auto operator==(optional const & x, U const & y) // noexcept(noexcept(convert_bool(*x == y))) // -> decltype(convert_bool(*x == y)) { return x && convert_bool(*x == y); } template constexpr auto operator==(T const & x, optional const & y) // noexcept(noexcept(convert_bool(x == *y))) // -> decltype(convert_bool(x == *y)) { return y && convert_bool(x == *y); } template constexpr auto operator!=(optional const & x, U const & y) // noexcept(noexcept(convert_bool(*x != y))) // -> decltype(convert_bool(*x != y)) { return !x || convert_bool(*x != y); } template constexpr auto operator!=(T const & x, optional const & y) // noexcept(noexcept(convert_bool(x != *y))) // -> decltype(convert_bool(x != *y)) { return !y || convert_bool(x != *y); } template constexpr auto operator<(optional const & x, U const & y) // noexcept(noexcept(convert_bool(*x < y))) // -> decltype(convert_bool(*x < y)) { return !x || convert_bool(*x < y); } template constexpr auto operator<(T const & x, optional const & y) // noexcept(noexcept(convert_bool(x < *y))) // -> decltype(convert_bool(x < *y)) { return y && convert_bool(x < *y); } template constexpr auto operator>(optional const & x, U const & y) // noexcept(noexcept(convert_bool(*x > y))) -> decltype(convert_bool(*x > y)) { return x && convert_bool(*x > y); } template constexpr auto operator>(T const & x, optional const & y) // noexcept(noexcept(convert_bool(x > *y))) // -> decltype(convert_bool(x > *y)) { return !y || convert_bool(x > *y); } template constexpr auto operator<=(optional const & x, U const & y) // noexcept(noexcept(convert_bool(*x <= y))) // -> decltype(convert_bool(*x <= y)) { return !x || convert_bool(*x <= y); } template constexpr auto operator<=(T const & x, optional const & y) // noexcept(noexcept(convert_bool(x <= *y))) // -> decltype(convert_bool(x <= *y)) { return y && convert_bool(x <= *y); } template constexpr auto operator>=(optional const & x, U const & y) // noexcept(noexcept(convert_bool(*x >= y))) // -> decltype(convert_bool(*x >= y)) { return x && convert_bool(*x >= y); } template constexpr auto operator>=(T const & x, optional const & y) // noexcept(noexcept(convert_bool(x >= *y))) // -> decltype(convert_bool(x >= *y)) { return !y || convert_bool(x >= *y); } // clang-format off template auto CPP_auto_fun(swap)(optional &x, optional &y) ( return x.swap(y) ) // clang-format on } // namespace optional_adl } // namespace detail /// \endcond // clang-format off template constexpr auto CPP_auto_fun(make_optional)(T &&t) ( return optional>{static_cast(t)} ) template constexpr auto CPP_auto_fun(make_optional)(Args &&... args) ( return optional{in_place, static_cast(args)...} ) template constexpr auto CPP_auto_fun(make_optional)(std::initializer_list il, Args &&... args) ( return optional{in_place, il, static_cast(args)...} ) // clang-format on /// \cond namespace detail { template struct non_propagating_cache : optional { non_propagating_cache() = default; constexpr non_propagating_cache(nullopt_t) noexcept {} constexpr non_propagating_cache(non_propagating_cache const &) noexcept : optional{} {} constexpr non_propagating_cache(non_propagating_cache && that) noexcept : optional{} { that.optional::reset(); } constexpr non_propagating_cache & operator=( non_propagating_cache const &) noexcept { optional::reset(); return *this; } constexpr non_propagating_cache & operator=( non_propagating_cache && that) noexcept { that.optional::reset(); optional::reset(); return *this; } using optional::operator=; }; template struct non_propagating_cache {}; } // namespace detail /// \endcond } // namespace ranges #endif