/// // optional - An implementation of std::optional with extensions // Written in 2017 by Simon Brand (@TartanLlama) // // To the extent possible under law, the author(s) have dedicated all // copyright and related and neighboring rights to this software to the // public domain worldwide. This software is distributed without any warranty. // // You should have received a copy of the CC0 Public Domain Dedication // along with this software. If not, see // . /// #include #include #include #include #include #if __cplusplus == 201103L || _MSC_VER == 1900 /// \exclude #define TL_OPTIONAL_11_CONSTEXPR #else /// \exclude #define TL_OPTIONAL_11_CONSTEXPR constexpr #endif #if _MSC_VER == 1900 /// \exclude #define TL_OPTIONAL_MSVC_2015_CONSTEXPR #else /// \exclude #define TL_OPTIONAL_MSVC_2015_CONSTEXPR constexpr #endif namespace tl { /// \brief Represents an optional with no data; essentially a bool class monostate {}; /// \brief A tag type to tell optional to construct its value in-place struct in_place_t { explicit in_place_t() = default; }; /// \brief A tag to tell optional to construct its value in-place static constexpr in_place_t in_place{}; template class optional; /// \exclude namespace detail { template using remove_cv_t = typename std::remove_cv::type; template using remove_const_t = typename std::remove_const::type; template using remove_volatile_t = typename std::remove_volatile::type; template using add_cv_t = typename std::add_cv::type; template using add_const_t = typename std::add_const::type; template using add_volatile_t = typename std::add_volatile::type; template using remove_reference_t = typename std::remove_reference::type; template using add_lvalue_reference_t = typename std::add_lvalue_reference::type; template using add_rvalue_reference_t = typename std::add_rvalue_reference::type; template using remove_pointer_t = typename std::remove_pointer::type; template using add_pointer_t = typename std::add_pointer::type; template using make_signed_t = typename std::make_signed::type; template using make_unsigned_t = typename std::make_unsigned::type; template using remove_extent_t = typename std::remove_extent::type; template using remove_all_extents_t = typename std::remove_all_extents::type; template using aligned_storage_t = typename std::aligned_storage::type; template using aligned_union_t = typename std::aligned_union::type; template using decay_t = typename std::decay::type; template using enable_if_t = typename std::enable_if::type; template using conditional_t = typename std::conditional::type; template using common_type_t = typename std::common_type::type; template using underlying_type_t = typename std::underlying_type::type; template using result_of_t = typename std::result_of::type; template struct conjunction : std::true_type {}; template struct conjunction : B {}; template struct conjunction : std::conditional, B>::type {}; template struct voider { using type = void; }; template using void_t = typename voider::type; template struct is_optional_impl : std::false_type {}; template struct is_optional_impl> : std::true_type {}; template using is_optional = is_optional_impl>; // https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround template >{}>, int = 0> constexpr auto invoke(Fn &&f, Args &&... args) noexcept( noexcept(std::mem_fn(f)(std::forward(args)...))) -> decltype(std::mem_fn(f)(std::forward(args)...)) { return std::mem_fn(f)(std::forward(args)...); } template >{}>> constexpr auto invoke(Fn &&f, Args &&... args) noexcept( noexcept(std::forward(f)(std::forward(args)...))) -> decltype(std::forward(f)(std::forward(args)...)) { return std::forward(f)(std::forward(args)...); } template struct invoke_result_impl; template struct invoke_result_impl< F, decltype(invoke(std::declval(), std::declval()...), void()), Us...> { using type = decltype(invoke(std::declval(), std::declval()...)); }; template using invoke_result = invoke_result_impl; template using invoke_result_t = typename invoke_result::type; template using fixup_void = conditional_t::value, monostate, U>; template struct get_invoke_optional_ret { using type = invoke_result_t< conditional_t::value, typename remove_reference_t::value_type &, typename remove_reference_t::value_type &&>, U...>; }; template using get_invoke_ret = typename conditional_t::value, get_invoke_optional_ret, invoke_result>::type; template using get_map_return = optional>>; template using returns_void = std::is_void>; template using disable_if_optional = enable_if_t::value>; template using enable_if_optional = enable_if_t::value>; template using enable_if_ret_void = enable_if_t::value>; template using disable_if_ret_void = enable_if_t::value>; template using enable_forward_value = detail::enable_if_t::value && !std::is_same, in_place_t>::value && !std::is_same, detail::decay_t>::value>; template using enable_from_other = detail::enable_if_t< std::is_constructible::value && !std::is_constructible &>::value && !std::is_constructible &&>::value && !std::is_constructible &>::value && !std::is_constructible &&>::value && !std::is_convertible &, T>::value && !std::is_convertible &&, T>::value && !std::is_convertible &, T>::value && !std::is_convertible &&, T>::value>; template using enable_assign_forward = detail::enable_if_t< !std::is_same, detail::decay_t>::value && !detail::conjunction, std::is_same>>::value && std::is_constructible::value && std::is_assignable::value>; template using enable_assign_from_other = detail::enable_if_t< std::is_constructible::value && std::is_assignable::value && !std::is_constructible &>::value && !std::is_constructible &&>::value && !std::is_constructible &>::value && !std::is_constructible &&>::value && !std::is_convertible &, T>::value && !std::is_convertible &&, T>::value && !std::is_convertible &, T>::value && !std::is_convertible &&, T>::value && !std::is_assignable &>::value && !std::is_assignable &&>::value && !std::is_assignable &>::value && !std::is_assignable &&>::value>; #ifdef _MSC_VER // TODO make a version which works with MSVC template struct is_swappable : std::true_type {}; template struct is_nothrow_swappable : std::true_type {}; #else // https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept namespace swap_adl_tests { // if swap ADL finds this then it would call std::swap otherwise (same // signature) struct tag {}; template tag swap(T &, T &); template tag swap(T (&a)[N], T (&b)[N]); // helper functions to test if an unqualified swap is possible, and if it // becomes std::swap template std::false_type can_swap(...) noexcept(false); template (), std::declval()))> std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), std::declval()))); template std::false_type uses_std(...); template std::is_same(), std::declval())), tag> uses_std(int); template struct is_std_swap_noexcept : std::integral_constant::value && std::is_nothrow_move_assignable::value> {}; template struct is_std_swap_noexcept : is_std_swap_noexcept {}; template struct is_adl_swap_noexcept : std::integral_constant(0))> {}; } // namespace swap_adl_tests template struct is_swappable : std::integral_constant< bool, decltype(detail::swap_adl_tests::can_swap(0))::value && (!decltype(detail::swap_adl_tests::uses_std(0))::value || (std::is_move_assignable::value && std::is_move_constructible::value))> {}; template struct is_swappable : std::integral_constant< bool, decltype(detail::swap_adl_tests::can_swap(0))::value && (!decltype( detail::swap_adl_tests::uses_std(0))::value || is_swappable::value)> {}; template struct is_nothrow_swappable : std::integral_constant< bool, is_swappable::value && ((decltype(detail::swap_adl_tests::uses_std(0))::value &&detail::swap_adl_tests::is_std_swap_noexcept::value) || (!decltype(detail::swap_adl_tests::uses_std(0))::value && detail::swap_adl_tests::is_adl_swap_noexcept::value))> { }; #endif } // namespace detail /// \brief A tag type to represent an empty optional struct nullopt_t { struct do_not_use {}; constexpr explicit nullopt_t(do_not_use, do_not_use) noexcept {} }; /// \brief Represents an empty optional /// \synopsis static constexpr nullopt_t nullopt; /// /// *Examples*: /// ``` /// tl::optional a = tl::nullopt; /// void foo (tl::optional); /// foo(tl::nullopt); //pass an empty optional /// ``` static constexpr nullopt_t nullopt{nullopt_t::do_not_use{}, nullopt_t::do_not_use{}}; class bad_optional_access : public std::exception { public: bad_optional_access() = default; const char *what() const noexcept { return "Optional has no value"; } }; /// \group relop /// \brief Compares two optional objects /// \details If both optionals contain a value, they are compared with `T`s relational operators. /// Otherwise `lhs` and `rhs` are equal only if they are both empty, and `lhs` is less than `rhs` /// only if `rhs` is empty and `lhs` is not. template inline constexpr bool operator==(const optional &lhs, const optional &rhs) { return lhs.has_value() == rhs.has_value() && (!lhs.has_value() || *lhs == *rhs); } /// \group relop template inline constexpr bool operator!=(const optional &lhs, const optional &rhs) { return lhs.has_value() != rhs.has_value() || (lhs.has_value() && *lhs != *rhs); } /// \group relop template inline constexpr bool operator<(const optional &lhs, const optional &rhs) { return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); } /// \group relop template inline constexpr bool operator>(const optional &lhs, const optional &rhs) { return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); } /// \group relop template inline constexpr bool operator<=(const optional &lhs, const optional &rhs) { return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); } /// \group relop template inline constexpr bool operator>=(const optional &lhs, const optional &rhs) { return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); } /// \group relop_nullopt /// \brief Compares an optional to a `nullopt` /// \details Equivalent to comparing the optional to an empty optional template inline constexpr bool operator==(const optional &lhs, nullopt_t) noexcept { return !lhs.has_value(); } /// \group relop_nullopt template inline constexpr bool operator==(nullopt_t, const optional &rhs) noexcept { return !rhs.has_value(); } /// \group relop_nullopt template inline constexpr bool operator!=(const optional &lhs, nullopt_t) noexcept { return lhs.has_value(); } /// \group relop_nullopt template inline constexpr bool operator!=(nullopt_t, const optional &rhs) noexcept { return rhs.has_value(); } /// \group relop_nullopt template inline constexpr bool operator<(const optional &, nullopt_t) noexcept { return false; } /// \group relop_nullopt template inline constexpr bool operator<(nullopt_t, const optional &rhs) noexcept { return rhs.has_value(); } /// \group relop_nullopt template inline constexpr bool operator<=(const optional &lhs, nullopt_t) noexcept { return !lhs.has_value(); } /// \group relop_nullopt template inline constexpr bool operator<=(nullopt_t, const optional &) noexcept { return true; } /// \group relop_nullopt template inline constexpr bool operator>(const optional &lhs, nullopt_t) noexcept { return lhs.has_value(); } /// \group relop_nullopt template inline constexpr bool operator>(nullopt_t, const optional &) noexcept { return false; } /// \group relop_nullopt template inline constexpr bool operator>=(const optional &, nullopt_t) noexcept { return true; } /// \group relop_nullopt template inline constexpr bool operator>=(nullopt_t, const optional &rhs) noexcept { return !rhs.has_value(); } /// \group relop_t /// \brief Compares the optional with a value. /// \details If the optional has a value, it is compared with the other value using `T`s relational operators. /// Otherwise, the optional is considered less than the value. template inline constexpr bool operator==(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs == rhs : false; } /// \group relop_t template inline constexpr bool operator==(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs == *rhs : false; } /// \group relop_t template inline constexpr bool operator!=(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs != rhs : true; } /// \group relop_t template inline constexpr bool operator!=(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs != *rhs : true; } /// \group relop_t template inline constexpr bool operator<(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs < rhs : true; } /// \group relop_t template inline constexpr bool operator<(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs < *rhs : false; } /// \group relop_t template inline constexpr bool operator<=(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs <= rhs : true; } /// \group relop_t template inline constexpr bool operator<=(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs <= *rhs : false; } /// \group relop_t template inline constexpr bool operator>(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs > rhs : false; } /// \group relop_t template inline constexpr bool operator>(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs > *rhs : true; } /// \group relop_t template inline constexpr bool operator>=(const optional &lhs, const U &rhs) { return lhs.has_value() ? *lhs >= rhs : false; } /// \group relop_t template inline constexpr bool operator>=(const U &lhs, const optional &rhs) { return rhs.has_value() ? lhs >= *rhs : true; } /// \synopsis template \nvoid swap(optional &lhs, optional &rhs); template ::value> * = nullptr, detail::enable_if_t::value> * = nullptr> void swap(optional &lhs, optional &rhs) noexcept(noexcept(lhs.swap(rhs))) { return lhs.swap(rhs); } template inline constexpr optional> make_optional(T &&v) { return optional>(std::forward(v)); } template inline constexpr optional make_optional(Args &&... args) { return optional(in_place, std::forward(args)...); } template inline constexpr optional make_optional(std::initializer_list il, Args &&... args) { return optional(in_place, il, std::forward(args)...); } } // namespace tl namespace std { // TODO SFINAE template struct hash> { ::std::size_t operator()(const tl::optional &o) const { if (!o.has_value()) return 0; return std::hash>()(*o); } }; } // namespace std namespace tl { /// \exclude namespace detail { template ::value> struct optional_storage_base { TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) {} template TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base(in_place_t, U &&... u) noexcept : m_value(std::forward(u)...), m_has_value(true) {} ~optional_storage_base() { if (m_has_value) { m_value.~T(); m_has_value = false; } } struct dummy {}; union { dummy m_dummy; T m_value; }; bool m_has_value; }; template struct optional_storage_base { TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) {} template TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base(in_place_t, U &&... u) noexcept : m_value(std::forward(u)...), m_has_value(true) {} ~optional_storage_base() = default; struct dummy {}; union { dummy m_dummy; T m_value; }; bool m_has_value = false; }; } // namespace detail /// An optional object is an object that contains the storage for another object and manages the lifetime of this contained object, if any. The contained object may be initialized after the optional object has been initialized, and may be destroyed before the optional object has been destroyed. The initialization state of the contained object is tracked by the optional object. template class optional : private detail::optional_storage_base { using base = detail::optional_storage_base; public: using value_type = T; /// Constructs an optional that does not contain a value. constexpr optional() noexcept = default; /// Constructs an optional that does not contain a value. constexpr optional(nullopt_t) noexcept {}; /// Copy constructor /// /// If `rhs` contains a value, the stored value is direct-initialized with it. /// Otherwise, the constructed optional is empty. TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) { if (rhs.has_value()) { this->m_has_value = true; new (std::addressof(this->m_value)) T(*rhs); } } // TODO conditionally disable /// Move constructor /// /// If `rhs` contains a value, the stored value is direct-initialized with it. /// Otherwise, the constructed optional is empty. TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) noexcept( std::is_nothrow_move_constructible::value) { if (rhs.has_value()) { this->m_has_value = true; new (std::addressof(this->m_value)) T(std::move(*rhs)); } } template constexpr explicit optional( detail::enable_if_t::value, in_place_t>, Args &&... args) : base(in_place, std::forward(args)...) {} template TL_OPTIONAL_11_CONSTEXPR explicit optional( detail::enable_if_t &, Args &&...>::value, in_place_t>, std::initializer_list il, Args &&... args) { this->m_has_value = true; new (std::addressof(this->m_value)) T(il, std::forward(args)...); } template < class U = T, detail::enable_if_t::value> * = nullptr, detail::enable_forward_value * = nullptr> constexpr optional(U &&u) : base(in_place, std::forward(u)) {} template < class U = T, detail::enable_if_t::value> * = nullptr, detail::enable_forward_value * = nullptr> constexpr explicit optional(U &&u) : base(in_place, std::forward(u)) {} template < class U, detail::enable_from_other * = nullptr, detail::enable_if_t::value> * = nullptr> optional(const optional &rhs) { this->m_has_value = true; new (std::addressof(this->m_value)) T(*rhs); } template * = nullptr, detail::enable_if_t::value> * = nullptr> optional(const optional &rhs) { this->m_has_value = true; new (std::addressof(this->m_value)) T(*rhs); } template < class U, detail::enable_from_other * = nullptr, detail::enable_if_t::value> * = nullptr> optional(optional &&rhs) { this->m_has_value = true; new (std::addressof(this->m_value)) T(std::move(*rhs)); } template < class U, detail::enable_from_other * = nullptr, detail::enable_if_t::value> * = nullptr> explicit optional(optional &&rhs) { this->m_has_value = true; new (std::addressof(this->m_value)) T(std::move(*rhs)); } ~optional() = default; optional &operator=(nullopt_t) noexcept { if (has_value()) { this->m_value.~T(); this->m_has_value = false; } } // TODO conditionally delete, check exception guarantee optional &operator=(const optional &rhs) { if (has_value()) { if (rhs.has_value()) { this->m_value = rhs.m_value; } else { this->m_value.~T(); this->m_has_value = false; } } if (rhs.has_value()) { new (std::addressof(this->m_value)) T(rhs.m_value); this->m_has_value = true; } } // TODO conditionally delete, check exception guarantee optional &operator=(optional &&rhs) noexcept( std::is_nothrow_move_assignable::value &&std::is_nothrow_move_constructible::value) { if (has_value()) { if (rhs.has_value()) { this->m_value = std::move(rhs.m_value); } else { this->m_value.~T(); this->m_has_value = false; } } if (rhs.has_value()) { new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); this->m_has_value = true; } } // TODO conditionally delete, check exception guarantee template * = nullptr> optional &operator=(U &&u) { if (has_value()) { this->m_value = std::forward(u); } else { new (std::addressof(this->m_value)) T(std::forward(u)); this->m_has_value = true; } } // TODO check exception guarantee template * = nullptr> optional &operator=(const optional &rhs) { if (has_value()) { if (rhs.has_value()) { this->m_value = rhs.m_value; } else { this->m_value.~T(); this->m_has_value = false; } } if (rhs.has_value()) { new (std::addressof(this->m_value)) T(rhs.m_value); this->m_has_value = true; } } // TODO check exception guarantee template * = nullptr> optional &operator=(optional &&rhs) { if (has_value()) { if (rhs.has_value()) { this->m_value = std::move(rhs.m_value); } else { this->m_value.~T(); this->m_has_value = false; } } if (rhs.has_value()) { new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); this->m_has_value = true; } } template T &emplace(Args &&... args) { static_assert(std::is_constructible::value, "T must be constructible with Args"); *this = nullopt; new (std::addressof(this->m_value)) T(std::forward(args)...); } template detail::enable_if_t< std::is_constructible &, Args &&...>::value, T &> emplace(std::initializer_list il, Args &&... args) { *this = nullopt; new (std::addressof(this->m_value)) T(il, std::forward(args)...); } void swap(optional &rhs) noexcept(std::is_nothrow_move_constructible::value &&detail::is_nothrow_swappable::value) { if (has_value()) { if (rhs.has_value()) { using std::swap; swap(**this, *rhs); } else { new (&rhs.m_value) T(std::move(this->m_value)); this->m_value.T::~T(); } } else if (rhs.has_value()) { new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); rhs.m_value.T::~T(); } } constexpr const T *operator->() const { return std::addressof(this->m_value); } TL_OPTIONAL_11_CONSTEXPR T *operator->() { return std::addressof(this->m_value); } constexpr const T &operator*() const & { return this->m_value; } TL_OPTIONAL_11_CONSTEXPR T &operator*() & { return this->m_value; } TL_OPTIONAL_11_CONSTEXPR T &&operator*() && { return std::move(this->m_value); } constexpr const T &&operator*() const && { return std::move(this->m_value); } constexpr explicit operator bool() const noexcept { return this->m_has_value; } constexpr bool has_value() const noexcept { return this->m_has_value; } TL_OPTIONAL_11_CONSTEXPR const T &value() const & { if (has_value()) return this->m_value; throw bad_optional_access(); } TL_OPTIONAL_11_CONSTEXPR T &value() & { if (has_value()) return this->m_value; throw bad_optional_access(); } TL_OPTIONAL_11_CONSTEXPR T &&value() && { if (has_value()) return std::move(this->m_value); throw bad_optional_access(); } constexpr const T &&value() const && { if (has_value()) return std::move(this->m_value); throw bad_optional_access(); } template constexpr T value_or(U &&u) const & { static_assert(std::is_copy_constructible::value && std::is_convertible::value, "T must be copy constructible and convertible from U"); return has_value() ? **this : static_cast(std::forward(u)); } template constexpr T value_or(U &&u) && { static_assert(std::is_move_constructible::value && std::is_convertible::value, "T must be move constructible and convertible from U"); return has_value() ? **this : static_cast(std::forward(u)); } void reset() noexcept { if (has_value()) { this->m_value.~T(); this->m_has_value = false; } } /// \group and_then /// Carries out some operation which returns an optional on the stored object if there is one. /// \requires `std::invoke(std::forward(f), value())` returns a `std::optional` for some `U`. /// \returns Let `U` be the result of `std::invoke(std::forward(f), value())`. Returns a `std::optional`. The return value is empty if `*this` is empty, otherwise the return value of `std::invoke(std::forward(f), value())` is returned. template TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } /// \group and_then_rval /// Carries out some operation which returns an optional on the stored object if there is one. /// \requires `std::invoke(std::forward(f), std::move(value()))` returns a `std::optional` for some `U`. /// \returns Let `U` be the result of `std::invoke(std::forward(f), std::move(value()))`. Returns a `std::optional`. The return value is empty if `*this` is empty, otherwise the return value of `std::invoke(std::forward(f), std::move(value()))` is returned. template TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); } /// \group and_then template constexpr detail::invoke_result_t and_then(F &&f) const & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } /// \group and_then_rval template constexpr detail::invoke_result_t and_then(F &&f) const && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); } /// \group map /// \synopsis template auto map(F &&f) &; /// \brief Carries out some operation on the stored object if there is one. /// \returns Let `U` be the result of `std::invoke(std::forward(f), value())`. Returns a `std::optional`. The return value is empty if `*this` is empty, otherwise an `optional` is constructed from the return value of `std::invoke(std::forward(f), value())` and is returned. template * = nullptr, detail::disable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) & noexcept(noexcept(detail::invoke(std::forward(f), std::declval()))) { using result = detail::get_map_return; return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) & { if (!has_value()) return nullopt; detail::invoke(std::forward(f), **this); return monostate{}; } template * = nullptr, detail::disable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) & { using result = detail::get_map_return; return (f.has_value() && has_value()) ? detail::invoke(*std::forward(f), **this) : result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) & { if (!f.has_value() || !has_value()) return nullopt; detail::invoke(*std::forward(f), **this); return monostate{}; } /// \group map_rval /// \synopsis template auto map(F &&f) &; /// \brief Carries out some operation on the stored object if there is one. /// \returns Let `U` be the result of `std::invoke(std::forward(f), std::move(value()))`. Returns a `std::optional`. The return value is empty if `*this` is empty, otherwise an `optional` is constructed from the return value of `std::invoke(std::forward(f), std::move(value()))` and is returned. template * = nullptr, detail::disable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) && { using result = detail::get_map_return; return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) && { if (!has_value()) return nullopt; detail::invoke(std::forward(f), std::move(**this)); return monostate{}; } template * = nullptr, detail::disable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) && { using result = detail::get_map_return; return (f.has_value() && has_value()) ? detail::invoke(*std::forward(f), std::move(**this)) : result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) && { if (!f.has_value() || !has_value()) return nullopt; detail::invoke(*std::forward(f), std::move(**this)); return monostate{}; } /// \group map /// \synopsis template auto map(F &&f) const &; template * = nullptr, detail::disable_if_ret_void * = nullptr> constexpr detail::get_map_return map(F &&f) const & { using result = detail::get_map_return; return this->has_value() ? result(detail::invoke(std::forward(f), **this)) : result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) const & { if (!has_value()) return nullopt; detail::invoke(std::forward(f), **this); return monostate{}; } template * = nullptr, detail::disable_if_ret_void * = nullptr> constexpr detail::get_map_return map(F &&f) const & { using result = detail::get_map_return; return (f.has_value() && has_value()) ? detail::invoke(*std::forward(f), **this) : result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) const & { if (!f.has_value() || !has_value()) return nullopt; detail::invoke(*std::forward(f), **this); return monostate{}; } /// \group map_rval /// \synopsis template auto map(F &&f) const &&; template * = nullptr, detail::disable_if_ret_void * = nullptr> constexpr detail::get_map_return map(F &&f) const && { using result = detail::get_map_return; return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) const && { if (!has_value()) return nullopt; detail::invoke(std::forward(f), std::move(**this)); return monostate{}; } template * = nullptr, detail::disable_if_ret_void * = nullptr> constexpr detail::get_map_return map(F &&f) const && { using result = detail::get_map_return; return (f.has_value() && has_value()) ? detail::invoke(*std::forward(f), std::move(**this)) : result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) const && { if (!f.has_value() || !has_value()) return nullopt; detail::invoke(*std::forward(f), std::move(**this)); return monostate{}; } /// \group or_else /// \brief Calls `f` if the optional is empty /// \synopsis template optional or_else (F &&f) &; /// \requires `std::invoke_result_t` must be void or convertible to `optional`. /// \effects If `*this` has a value, returns `*this`. Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns `std::nullopt`. Otherwise, returns `std::forward(f)()`. template * = nullptr> optional TL_OPTIONAL_MSVC_2015_CONSTEXPR or_else(F &&f) & { if (has_value()) return *this; std::forward(f)(); return nullopt; } template * = nullptr> optional TL_OPTIONAL_MSVC_2015_CONSTEXPR or_else(F &&f) & { return has_value() ? *this : std::forward(f)(); } /// \group or_else_rval /// \brief Calls `f` if the optional is empty /// \synopsis template optional or_else (F &&f) &&; /// \requires `std::invoke_result_t` must be void or convertible to `optional`. /// \effects If `*this` has a value, returns `std::move(*this)`. Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns `std::nullopt`. Otherwise, returns `std::forward(f)()`. template * = nullptr> optional or_else(F &&f) && { if (has_value()) return std::move(*this); std::forward(f)(); return nullopt; } template * = nullptr> optional TL_OPTIONAL_MSVC_2015_CONSTEXPR or_else(F &&f) && { return has_value() ? std::move(*this) : std::forward(f)(); } /// \group or_else /// \synopsis template optional or_else (F &&f) const &; template * = nullptr> optional or_else(F &&f) const & { if (has_value()) return *this; std::forward(f)(); return nullopt; } template * = nullptr> optional TL_OPTIONAL_MSVC_2015_CONSTEXPR or_else(F &&f) const & { return has_value() ? *this : std::forward(f)(); } /// \group or_else_rval /// \synopsis template optional or_else (F &&f) const &&; template * = nullptr> optional or_else(F &&f) const && { if (has_value()) return std::move(*this); std::forward(f)(); return nullopt; } template * = nullptr> optional or_else(F &&f) const && { return has_value() ? std::move(*this) : std::forward(f)(); } template U map_or(F &&f, U &&u) & { return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u); } template U map_or(F &&f, U &&u) && { return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u); } template U map_or(F &&f, U &&u) const & { return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u); } template U map_or(F &&f, U &&u) const && { return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u); } template U map_or_else(F &&f, U &&u) & { return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u)(); } template U map_or_else(F &&f, U &&u) && { return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u)(); } template U map_or_else(F &&f, U &&u) const & { return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u)(); } template U map_or_else(F &&f, U &&u) const && { return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u)(); } }; // template optional(T)->optional; } // namespace tl