// 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