// Copyright (C) 2023 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_OPTIONAL_H #define DLIB_OPTIONAL_H #include <exception> #include <initializer_list> #include "functional.h" namespace dlib { // --------------------------------------------------------------------------------------------------- class bad_optional_access : public std::exception { public: bad_optional_access() = default; const char *what() const noexcept { return "Optional has no value"; } }; // --------------------------------------------------------------------------------------------------- struct nullopt_t { nullopt_t() = delete; constexpr explicit nullopt_t(int) noexcept {} }; static constexpr nullopt_t nullopt{int{}}; // --------------------------------------------------------------------------------------------------- template<class T> class optional; /*! WHAT THIS OBJECT REPRESENTS This is a standard's compliant backport of std::optional that works with C++14. It includes C++23 monadic interfaces Therefore, refer to https://en.cppreference.com/w/cpp/utility/optional for docs on the interface of optional. !*/ // --------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------- // IMPLEMENTATION DETAILS BELOW // --------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------- namespace details { template<class T> struct is_optional : std::false_type{}; template<class T> struct is_optional<dlib::optional<T>> : std::true_type{}; template<class T, class U> using is_constructible_from = And< !std::is_constructible<T, dlib::optional<U>&>::value, !std::is_constructible<T, const dlib::optional<U>&>::value, !std::is_constructible<T, dlib::optional<U>&&>::value, !std::is_constructible<T, const dlib::optional<U>&&>::value, !std::is_convertible< dlib::optional<U>&, T>::value, !std::is_convertible<const dlib::optional<U>&, T>::value, !std::is_convertible< dlib::optional<U>&&, T>::value, !std::is_convertible<const dlib::optional<U>&&, T>::value >; template<class T, class U> using is_assignable_from = And< is_constructible_from<T,U>::value, std::is_assignable<T&, dlib::optional<U>&>::value, std::is_assignable<T&, const dlib::optional<U>&>::value, std::is_assignable<T&, dlib::optional<U>&&>::value, std::is_assignable<T&, const dlib::optional<U>&&>::value >; template<class T, class U> using is_copy_convertible = std::enable_if_t< is_constructible_from<T,U>::value && std::is_constructible<T, const U&>::value, bool >; template<class T, class U> using is_move_convertible = std::enable_if_t< is_constructible_from<T,U>::value && std::is_constructible<T, U&&>::value, bool >; template<class T, class U> using is_copy_assignable = std::enable_if_t< is_assignable_from<T,U>::value && std::is_constructible<T, const U&>::value && std::is_assignable<T&, const U&>::value, bool >; template<class T, class U> using is_move_assignable = std::enable_if_t< is_assignable_from<T,U>::value && std::is_constructible<T, U>::value && std::is_assignable<T&, U>::value, bool >; template<class T, class U, class U_ = std::decay_t<U>> using is_construct_convertible_from = std::enable_if_t< std::is_constructible<T, U&&>::value && !std::is_same<U_, in_place_t>::value && !std::is_same<U_, dlib::optional<T>>::value, bool >; template <class T, class U, class U_ = std::decay_t<U>> using is_assign_convertible_from = std::enable_if_t< std::is_constructible<T, U>::value && std::is_assignable<T&, U>::value && !std::is_same<U_, dlib::optional<T>>::value && (!std::is_scalar<T>::value || !std::is_same<T, U_>::value), bool >; // --------------------------------------------------------------------------------------------------- template < class T, bool = std::is_trivially_destructible<T>::value > struct optional_storage { constexpr optional_storage() noexcept : e{}, active{false} {} template<class ...U> constexpr optional_storage(in_place_t, U&& ...u) noexcept(std::is_nothrow_constructible<T,U...>::value) : val{std::forward<U>(u)...}, active{true} {} ~optional_storage() noexcept(std::is_nothrow_destructible<T>::value) { if (active) val.~T(); } struct empty{}; union {T val; empty e;}; bool active{false}; }; template <class T> struct optional_storage<T, true> { constexpr optional_storage() noexcept : e{}, active{false} {} template<class ...U> constexpr optional_storage(in_place_t, U&& ...u) noexcept(std::is_nothrow_constructible<T,U...>::value) : val{std::forward<U>(u)...}, active{true} {} struct empty{}; union {T val; empty e;}; bool active{false}; }; // --------------------------------------------------------------------------------------------------- template <class T> struct optional_ops : optional_storage<T> { using optional_storage<T>::optional_storage; template <class... U> constexpr void construct(U&&... u) noexcept(std::is_nothrow_constructible<T,U...>::value) { new (std::addressof(this->val)) T(std::forward<U>(u)...); this->active = true; } template<class Optional> constexpr void assign(Optional&& rhs) noexcept(std::is_nothrow_constructible<T,Optional>::value && std::is_nothrow_assignable<T&,Optional>::value) { if (this->active && rhs.active) this->val = std::forward<Optional>(rhs).val; else if (!this->active && rhs.active) construct(std::forward<Optional>(rhs).val); else if (this->active && !rhs.active) destruct(); } constexpr void destruct() noexcept(std::is_nothrow_destructible<T>::value) { if (this->active) { this->val.~T(); this->active = false; } } }; // --------------------------------------------------------------------------------------------------- template <class T, bool = std::is_trivially_copy_constructible<T>::value> struct optional_copy : optional_ops<T> { using optional_ops<T>::optional_ops; }; template <class T> struct optional_copy<T, false> : optional_ops<T> { using optional_ops<T>::optional_ops; constexpr optional_copy() = default; constexpr optional_copy(optional_copy&& rhs) = default; constexpr optional_copy &operator=(const optional_copy& rhs) = default; constexpr optional_copy &operator=(optional_copy&& rhs) = default; constexpr optional_copy(const optional_copy& rhs) noexcept(std::is_nothrow_copy_constructible<T>::value) : optional_ops<T>() { if (rhs.active) this->construct(rhs.val); } }; // --------------------------------------------------------------------------------------------------- template <class T, bool = std::is_trivially_move_constructible<T>::value> struct optional_move : optional_copy<T> { using optional_copy<T>::optional_copy; }; template <class T> struct optional_move<T, false> : optional_copy<T> { using optional_copy<T>::optional_copy; constexpr optional_move() = default; constexpr optional_move(const optional_move& rhs) = default; constexpr optional_move& operator=(const optional_move& rhs) = default; constexpr optional_move& operator=(optional_move&& rhs) = default; constexpr optional_move(optional_move&& rhs) noexcept(std::is_nothrow_move_constructible<T>::value) { if (rhs.active) this->construct(std::move(rhs.val)); } }; // --------------------------------------------------------------------------------------------------- template < class T, bool = std::is_trivially_copy_assignable<T>::value && std::is_trivially_copy_constructible<T>::value && std::is_trivially_destructible<T>::value > struct optional_copy_assign : optional_move<T> { using optional_move<T>::optional_move; }; template <class T> struct optional_copy_assign<T, false> : optional_move<T> { using optional_move<T>::optional_move; constexpr optional_copy_assign() = default; constexpr optional_copy_assign(const optional_copy_assign& rhs) = default; constexpr optional_copy_assign(optional_copy_assign&& rhs) = default; constexpr optional_copy_assign& operator=(optional_copy_assign &&rhs) = default; constexpr optional_copy_assign& operator=(const optional_copy_assign &rhs) noexcept(std::is_nothrow_copy_constructible<T>::value && std::is_nothrow_copy_assignable<T>::value) { this->assign(rhs); return *this; } }; // --------------------------------------------------------------------------------------------------- template < class T, bool = std::is_trivially_destructible<T>::value && std::is_trivially_move_constructible<T>::value && std::is_trivially_move_assignable<T>::value > struct optional_move_assign : optional_copy_assign<T> { using optional_copy_assign<T>::optional_copy_assign; }; template <class T> struct optional_move_assign<T, false> : optional_copy_assign<T> { using optional_copy_assign<T>::optional_copy_assign; constexpr optional_move_assign() = default; constexpr optional_move_assign(const optional_move_assign &rhs) = default; constexpr optional_move_assign(optional_move_assign &&rhs) = default; constexpr optional_move_assign& operator=(const optional_move_assign &rhs) = default; constexpr optional_move_assign& operator=(optional_move_assign &&rhs) noexcept(std::is_nothrow_move_constructible<T>::value && std::is_nothrow_move_assignable<T>::value) { this->assign(std::move(rhs)); return *this; } }; // --------------------------------------------------------------------------------------------------- template < class T, bool copyable = std::is_copy_constructible<T>::value, bool moveable = std::is_move_constructible<T>::value > struct optional_delete_constructors { constexpr optional_delete_constructors() = default; constexpr optional_delete_constructors(const optional_delete_constructors&) = default; constexpr optional_delete_constructors(optional_delete_constructors&&) = default; constexpr optional_delete_constructors& operator=(const optional_delete_constructors &) = default; constexpr optional_delete_constructors& operator=(optional_delete_constructors &&) = default; }; template <class T> struct optional_delete_constructors<T, true, false> { constexpr optional_delete_constructors() = default; constexpr optional_delete_constructors(const optional_delete_constructors&) = default; constexpr optional_delete_constructors(optional_delete_constructors&&) = delete; constexpr optional_delete_constructors& operator=(const optional_delete_constructors &) = default; constexpr optional_delete_constructors& operator=(optional_delete_constructors &&) = default; }; template <class T> struct optional_delete_constructors<T, false, true> { constexpr optional_delete_constructors() = default; constexpr optional_delete_constructors(const optional_delete_constructors&) = delete; constexpr optional_delete_constructors(optional_delete_constructors&&) = default; constexpr optional_delete_constructors& operator=(const optional_delete_constructors &) = default; constexpr optional_delete_constructors& operator=(optional_delete_constructors &&) = default; }; template <class T> struct optional_delete_constructors<T, false, false> { constexpr optional_delete_constructors() = default; constexpr optional_delete_constructors(const optional_delete_constructors&) = delete; constexpr optional_delete_constructors(optional_delete_constructors&&) = delete; constexpr optional_delete_constructors& operator=(const optional_delete_constructors&) = default; constexpr optional_delete_constructors& operator=(optional_delete_constructors &&) = default; }; // --------------------------------------------------------------------------------------------------- template < class T, bool copyable = (std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value), bool moveable = (std::is_move_constructible<T>::value && std::is_move_assignable<T>::value) > struct optional_delete_assign { constexpr optional_delete_assign() = default; constexpr optional_delete_assign(const optional_delete_assign &) = default; constexpr optional_delete_assign(optional_delete_assign &&) = default; constexpr optional_delete_assign& operator=(const optional_delete_assign &) = default; constexpr optional_delete_assign& operator=(optional_delete_assign &&) = default; }; template <class T> struct optional_delete_assign<T, true, false> { constexpr optional_delete_assign() = default; constexpr optional_delete_assign(const optional_delete_assign &) = default; constexpr optional_delete_assign(optional_delete_assign &&) = default; constexpr optional_delete_assign& operator=(const optional_delete_assign &) = default; constexpr optional_delete_assign& operator=(optional_delete_assign &&) = delete; }; template <class T> struct optional_delete_assign<T, false, true> { constexpr optional_delete_assign() = default; constexpr optional_delete_assign(const optional_delete_assign &) = default; constexpr optional_delete_assign(optional_delete_assign &&) = default; constexpr optional_delete_assign& operator=(const optional_delete_assign &) = delete; constexpr optional_delete_assign& operator=(optional_delete_assign &&) = default; }; template <class T> struct optional_delete_assign<T, false, false> { constexpr optional_delete_assign() = default; constexpr optional_delete_assign(const optional_delete_assign &) = default; constexpr optional_delete_assign(optional_delete_assign &&) = default; constexpr optional_delete_assign& operator=(const optional_delete_assign &) = delete; constexpr optional_delete_assign& operator=(optional_delete_assign &&) = delete; }; // --------------------------------------------------------------------------------------------------- } // --------------------------------------------------------------------------------------------------- template <class T> class optional : private details::optional_move_assign<T>, private details::optional_delete_constructors<T>, private details::optional_delete_assign<T> { using base = details::optional_move_assign<T>; static_assert(!std::is_reference<T>::value, "optional<T&> not allowed"); static_assert(!std::is_same<T, in_place_t>::value, "optional<in_place_t> not allowed"); static_assert(!std::is_same<T, nullopt_t>::value, "optional<nullopt_t> not allowed"); public: using value_type = T; constexpr optional() = default; constexpr optional(const optional &rhs) = default; constexpr optional(optional &&rhs) = default; constexpr optional& operator=(const optional &rhs) = default; constexpr optional& operator=(optional &&rhs) = default; ~optional() = default; constexpr optional(nullopt_t) noexcept {} constexpr optional& operator=(nullopt_t) noexcept { if (*this) reset(); return *this; } template < class U, details::is_copy_convertible<T,U> = true, std::enable_if_t<!std::is_convertible<const U&, T>::value, bool> = true > constexpr explicit optional(const optional<U> &rhs) noexcept(std::is_nothrow_constructible<T,const U&>::value) { if (rhs) this->construct(*rhs); } template < class U, details::is_copy_convertible<T,U> = true, std::enable_if_t<std::is_convertible<const U&, T>::value, bool> = true > constexpr optional(const optional<U> &rhs) noexcept(std::is_nothrow_constructible<T,const U&>::value) { if (rhs) this->construct(*rhs); } template < class U, details::is_move_convertible<T,U> = true, std::enable_if_t<!std::is_convertible<U&&, T>::value, bool> = true > constexpr explicit optional(optional<U>&& rhs) noexcept(std::is_nothrow_constructible<T,U&&>::value) { if (rhs) this->construct(std::move(*rhs)); } template < class U, details::is_move_convertible<T,U> = true, std::enable_if_t<std::is_convertible<U&&, T>::value, bool> = true > constexpr optional(optional<U>&& rhs) noexcept(std::is_nothrow_constructible<T,U&&>::value) { if (rhs) this->construct(std::move(*rhs)); } template < class... Args, std::enable_if_t<std::is_constructible<T, Args...>::value, bool> = true > constexpr explicit optional ( in_place_t, Args&&... args ) noexcept(std::is_nothrow_constructible<T,Args&&...>::value) : base(in_place, std::forward<Args>(args)...) { } template < class U, class... Args, std::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value, bool> = true > constexpr explicit optional ( in_place_t, std::initializer_list<U> il, Args &&... args ) noexcept(std::is_nothrow_constructible<T,std::initializer_list<U>&,Args&&...>::value) { this->construct(il, std::forward<Args>(args)...); } template< class U, details::is_construct_convertible_from<T,U> = true, std::enable_if_t<!std::is_convertible<U&&, T>::value, bool> = true > constexpr explicit optional(U &&u) noexcept(std::is_nothrow_constructible<T, U&&>::value) : base(in_place, std::forward<U>(u)) { } template< class U, details::is_construct_convertible_from<T,U> = true, std::enable_if_t<std::is_convertible<U&&, T>::value, bool> = true > constexpr optional(U &&u) noexcept(std::is_nothrow_constructible<T, U&&>::value) : base(in_place, std::forward<U>(u)) { } template < class U, details::is_copy_assignable<T, U> = true > constexpr optional &operator=(const optional<U>& rhs) noexcept(std::is_nothrow_constructible<T, const U&>::value && std::is_nothrow_assignable<T, const U&>::value) { this->assign(rhs); return *this; } template < class U, details::is_move_assignable<T, U> = true > constexpr optional &operator=(optional<U>&& rhs) noexcept(std::is_nothrow_constructible<T, U>::value && std::is_nothrow_assignable<T, U>::value) { this->assign(std::move(rhs)); return *this; } template < class U, details::is_assign_convertible_from<T,U> = true > constexpr optional& operator=(U &&u) noexcept(std::is_nothrow_constructible<T, U>::value && std::is_nothrow_assignable<T, U>::value) { if (*this) **this = std::forward<U>(u); else this->construct(std::forward<U>(u)); return *this; } template <class... Args> constexpr T& emplace(Args &&... args) noexcept(std::is_nothrow_constructible<T, Args...>::value) { reset(); this->construct(std::forward<Args>(args)...); return **this; } template <class U, class... Args> constexpr T& emplace(std::initializer_list<U> il, Args &&... args) { reset(); this->construct(il, std::forward<Args>(args)...); return **this; } void swap(optional& rhs) noexcept(std::is_nothrow_move_constructible<T>::value && dlib::is_nothrow_swappable<T>::value) { using std::swap; if (*this && rhs) { swap(**this, *rhs); } else if (*this && !rhs) { rhs = std::move(**this); reset(); } else if (!*this && rhs) { *this = std::move(*rhs); rhs.reset(); } } constexpr const T* operator->() const noexcept { return &this->val; } constexpr T* operator->() noexcept { return &this->val; } constexpr T& operator*() & noexcept { return this->val; } constexpr const T& operator*() const& noexcept { return this->val; } constexpr T&& operator*() && noexcept { return std::move(this->val); } constexpr const T&& operator*() const&& noexcept { return std::move(this->val); } constexpr explicit operator bool() const noexcept { return this->active; } constexpr bool has_value() const noexcept { return this->active; } constexpr T& value() & { if (*this) return **this; throw bad_optional_access(); } constexpr const T& value() const & { if (*this) return **this; throw bad_optional_access(); } constexpr T&& value() && { if (*this) return std::move(**this); throw bad_optional_access(); } constexpr const T&& value() const && { if (*this) return std::move(**this); throw bad_optional_access(); } template <class U> constexpr T value_or(U &&u) const & { return *this ? **this : static_cast<T>(std::forward<U>(u)); } template <class U> constexpr T value_or(U &&u) && { return *this ? std::move(**this) : static_cast<T>(std::forward<U>(u)); } void reset() noexcept(std::is_nothrow_destructible<T>::value) { this->destruct(); } template < class F, class Return = dlib::remove_cvref_t<dlib::invoke_result_t<F,T&>>, std::enable_if_t<details::is_optional<Return>::value, bool> = true > constexpr auto and_then(F&& f) & { if (*this) return dlib::invoke(std::forward<F>(f), **this); else return Return{}; } template < class F, class Return = dlib::remove_cvref_t<dlib::invoke_result_t<F,const T&>>, std::enable_if_t<details::is_optional<Return>::value, bool> = true > constexpr auto and_then(F&& f) const& { if (*this) return dlib::invoke(std::forward<F>(f), **this); else return Return{}; } template < class F, class Return = dlib::remove_cvref_t<dlib::invoke_result_t<F,T>>, std::enable_if_t<details::is_optional<Return>::value, bool> = true > constexpr auto and_then(F&& f) && { if (*this) return dlib::invoke(std::forward<F>(f), std::move(**this)); else return Return{}; } template < class F, class Return = dlib::remove_cvref_t<dlib::invoke_result_t<F,const T>>, std::enable_if_t<details::is_optional<Return>::value, bool> = true > constexpr auto and_then(F&& f) const&& { if (*this) return dlib::invoke(std::forward<F>(f), std::move(**this)); else return Return{}; } template < class F, class U = dlib::remove_cvref_t<dlib::invoke_result_t<F, T&>>, std::enable_if_t<!std::is_same<U, dlib::in_place_t>::value, bool> = true, std::enable_if_t<!std::is_same<U, dlib::nullopt_t>::value, bool> = true > constexpr dlib::optional<U> transform(F&& f) & { if (*this) return dlib::invoke(std::forward<F>(f), **this); else return dlib::optional<U>{}; } template < class F, class U = dlib::remove_cvref_t<dlib::invoke_result_t<F, const T&>>, std::enable_if_t<!std::is_same<U, dlib::in_place_t>::value, bool> = true, std::enable_if_t<!std::is_same<U, dlib::nullopt_t>::value, bool> = true > constexpr dlib::optional<U> transform(F&& f) const& { if (*this) return dlib::invoke(std::forward<F>(f), **this); else return dlib::optional<U>{}; } template < class F, class U = dlib::remove_cvref_t<dlib::invoke_result_t<F,T>>, std::enable_if_t<!std::is_same<U, dlib::in_place_t>::value, bool> = true, std::enable_if_t<!std::is_same<U, dlib::nullopt_t>::value, bool> = true > constexpr dlib::optional<U> transform(F&& f) && { if (*this) return dlib::invoke(std::forward<F>(f), std::move(**this)); else return dlib::optional<U>{}; } template < class F, class U = dlib::remove_cvref_t<dlib::invoke_result_t<F,const T>>, std::enable_if_t<!std::is_same<U, dlib::in_place_t>::value, bool> = true, std::enable_if_t<!std::is_same<U, dlib::nullopt_t>::value, bool> = true > constexpr dlib::optional<U> transform(F&& f) const&& { if (*this) return dlib::invoke(std::forward<F>(f), std::move(**this)); else return dlib::optional<U>{}; } template < class F, class U = dlib::remove_cvref_t<dlib::invoke_result_t<F>>, std::enable_if_t<std::is_same<U, dlib::optional<T>>::value, bool> = true > constexpr optional or_else( F&& f ) const& { return *this ? *this : std::forward<F>(f)(); } template < class F, class U = dlib::remove_cvref_t<dlib::invoke_result_t<F>>, std::enable_if_t<std::is_same<U, dlib::optional<T>>::value, bool> = true > constexpr optional or_else( F&& f ) && { return *this ? std::move(*this) : std::forward<F>(f)(); } }; // --------------------------------------------------------------------------------------------------- template <class T, class U> constexpr bool operator==(const optional<T> &lhs, const optional<U> &rhs) noexcept(noexcept(std::declval<T>() == std::declval<T>())) { return bool(lhs) == bool(rhs) && (!bool(lhs) || *lhs == *rhs); } template <class T, class U> constexpr bool operator!=(const optional<T> &lhs, const optional<U> &rhs) noexcept(noexcept(std::declval<T>() != std::declval<T>())) { return !(lhs == rhs); } template <class T, class U> constexpr bool operator<(const optional<T> &lhs, const optional<U> &rhs) noexcept(noexcept(std::declval<T>() < std::declval<T>())) { return bool(rhs) && (!bool(lhs) || *lhs < *rhs); } template <class T, class U> constexpr bool operator>=(const optional<T> &lhs, const optional<U> &rhs) noexcept(noexcept(std::declval<T>() >= std::declval<T>())) { return !(lhs < rhs); } template <class T, class U> constexpr bool operator>(const optional<T> &lhs, const optional<U> &rhs) noexcept(noexcept(std::declval<T>() > std::declval<T>())) { return bool(lhs) && (!bool(rhs) || *lhs > *rhs); } template <class T, class U> constexpr bool operator<=(const optional<T> &lhs, const optional<U> &rhs) noexcept(noexcept(std::declval<T>() <= std::declval<T>())) { return !(lhs > rhs); } // --------------------------------------------------------------------------------------------------- template <class T> constexpr bool operator==(const optional<T> &lhs, nullopt_t) noexcept { return !bool(lhs); } template <class T> constexpr bool operator==(nullopt_t, const optional<T> &rhs) noexcept { return !bool(rhs); } template <class T> constexpr bool operator!=(const optional<T> &lhs, nullopt_t) noexcept { return bool(lhs); } template <class T> constexpr bool operator!=(nullopt_t, const optional<T> &rhs) noexcept { return bool(rhs); } template <class T> constexpr bool operator<(const optional<T> &, nullopt_t) noexcept { return false; } template <class T> constexpr bool operator<(nullopt_t, const optional<T> &rhs) noexcept { return bool(rhs); } template <class T> constexpr bool operator<=(const optional<T> &lhs, nullopt_t) noexcept { return !bool(lhs); } template <class T> constexpr bool operator<=(nullopt_t, const optional<T> &) noexcept { return true; } template <class T> constexpr bool operator>(const optional<T> &lhs, nullopt_t) noexcept { return bool(lhs); } template <class T> constexpr bool operator>(nullopt_t, const optional<T> &) noexcept { return false; } template <class T> constexpr bool operator>=(const optional<T> &, nullopt_t) noexcept { return true; } template <class T> constexpr bool operator>=(nullopt_t, const optional<T> &rhs) noexcept { return !rhs.has_value(); } // --------------------------------------------------------------------------------------------------- template <class T, class U> constexpr bool operator==(const optional<T> &lhs, const U &rhs) { return bool(lhs) ? *lhs == rhs : false; } template <class T, class U> constexpr bool operator==(const U &lhs, const optional<T> &rhs) { return bool(rhs) ? lhs == *rhs : false; } template <class T, class U> constexpr bool operator!=(const optional<T> &lhs, const U &rhs) { return bool(lhs) ? *lhs != rhs : true; } template <class T, class U> constexpr bool operator!=(const U &lhs, const optional<T> &rhs) { return bool(rhs) ? lhs != *rhs : true; } template <class T, class U> constexpr bool operator<(const optional<T> &lhs, const U &rhs) { return bool(lhs) ? *lhs < rhs : true; } template <class T, class U> constexpr bool operator<(const U &lhs, const optional<T> &rhs) { return bool(rhs) ? lhs < *rhs : false; } template <class T, class U> constexpr bool operator<=(const optional<T> &lhs, const U &rhs) { return bool(lhs) ? *lhs <= rhs : true; } template <class T, class U> constexpr bool operator<=(const U &lhs, const optional<T> &rhs) { return bool(rhs) ? lhs <= *rhs : false; } template <class T, class U> constexpr bool operator>(const optional<T> &lhs, const U &rhs) { return bool(lhs) ? *lhs > rhs : false; } template <class T, class U> constexpr bool operator>(const U &lhs, const optional<T> &rhs) { return bool(rhs) ? lhs > *rhs : true; } template <class T, class U> constexpr bool operator>=(const optional<T> &lhs, const U &rhs) { return bool(lhs) ? *lhs >= rhs : false; } template <class T, class U> constexpr bool operator>=(const U &lhs, const optional<T> &rhs) { return bool(rhs) ? lhs >= *rhs : true; } // --------------------------------------------------------------------------------------------------- template < class T, std::enable_if_t< std::is_move_constructible<T>::value && dlib::is_swappable<T>::value, bool> = true > void swap(dlib::optional<T>& lhs, dlib::optional<T>& rhs) noexcept(noexcept(lhs.swap(rhs))) { return lhs.swap(rhs); } // --------------------------------------------------------------------------------------------------- template< class T > constexpr dlib::optional<std::decay_t<T>> make_optional( T&& value ) { return dlib::optional<std::decay_t<T>>(std::forward<T>(value)); } template< class T, class... Args > constexpr dlib::optional<T> make_optional( Args&&... args ) { return dlib::optional<T>(dlib::in_place, std::forward<Args>(args)...); } template< class T, class U, class... Args > constexpr dlib::optional<T> make_optional( std::initializer_list<U> il, Args&&... args ) { return dlib::optional<T>(dlib::in_place, il, std::forward<Args>(args)...); } // --------------------------------------------------------------------------------------------------- } #endif