blob: b434bceb226cf06f412710e885f378240373cb58 [file]
// <optional> -*- C++ -*-
// Copyright (C) 2013-2026 Free Software Foundation, Inc.
// Copyright The GNU Toolchain Authors.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file include/optional
* This is a Standard C++ Library header.
*/
#ifndef GCC_BACKPORT_GLIBCXX_OPTIONAL
#define GCC_BACKPORT_GLIBCXX_OPTIONAL 1
#define WANT_FUNCTIONAL
#include "gbits/check_system_h.h"
#include <type_traits>
#include <initializer_list>
#include "gbits/assert.h"
#include "gbits/enable_special_members.h"
#include "gbits/functional_hash.h"
#include "gbits/exception_defines.h"
#include "gbits/invoke.h"
#include "gbits/optional_ref.h" // nullopt_t, optional<_Tp&>
#include "gbits/stl_construct.h" // _Construct
#ifdef GCC_BACKPORT_WANT_EXCEPTIONS
# include <exception>
#endif
#include "optionalfwd"
namespace gcc
{
/**
* @addtogroup utilities
* @{
*/
#ifdef GCC_BACKPORT_WANT_EXCEPTIONS
/**
* @brief Exception class thrown when a disengaged optional object is
* dereferenced.
* @ingroup exceptions
*/
class bad_optional_access : public std::exception
{
public:
bad_optional_access() = default;
virtual ~bad_optional_access() = default;
#if __cpp_lib_constexpr_exceptions >= 202502L
constexpr
#endif
const char* what() const noexcept override
{ return "bad optional access"; }
};
#endif
// XXX Does not belong here.
[[__noreturn__]]
#if __cpp_lib_constexpr_exceptions >= 202502L
constexpr
#else
inline
#endif
void
g_throw_bad_optional_access()
{ g_throw_or_abort(bad_optional_access()); }
// This class template manages construction/destruction of
// the contained value for a std::optional.
template <typename G_Tp>
struct G_Optional_payload_base
{
using G_Stored_type = std::remove_const_t<G_Tp>;
G_Optional_payload_base() = default;
~G_Optional_payload_base() = default;
template<typename... G_Args>
constexpr
G_Optional_payload_base(in_place_t g_tag, G_Args&&... g_args)
: m_payload(g_tag, std::forward<G_Args>(g_args)...),
m_engaged(true)
{ }
template<typename G_Up, typename... G_Args>
constexpr
G_Optional_payload_base(std::initializer_list<G_Up> g_il,
G_Args&&... g_args)
: m_payload(g_il, std::forward<G_Args>(g_args)...),
m_engaged(true)
{ }
// Constructor used by _Optional_base copy constructor when the
// contained value is not trivially copy constructible.
constexpr
G_Optional_payload_base(bool /* __engaged */,
const G_Optional_payload_base& g_other)
{
if (g_other.m_engaged)
this->m_construct(g_other.m_get());
}
// Constructor used by _Optional_base move constructor when the
// contained value is not trivially move constructible.
constexpr
G_Optional_payload_base(bool /* __engaged */,
G_Optional_payload_base&& g_other)
{
if (g_other.m_engaged)
this->m_construct(std::move(g_other.m_get()));
}
// Copy constructor is only used to when the contained value is
// trivially copy constructible.
G_Optional_payload_base(const G_Optional_payload_base&) = default;
// Move constructor is only used to when the contained value is
// trivially copy constructible.
G_Optional_payload_base(G_Optional_payload_base&&) = default;
G_Optional_payload_base&
operator=(const G_Optional_payload_base&) = default;
G_Optional_payload_base&
operator=(G_Optional_payload_base&&) = default;
// used to perform non-trivial copy assignment.
constexpr void
m_copy_assign(const G_Optional_payload_base& g_other)
{
if (this->m_engaged && g_other.m_engaged)
this->m_get() = g_other.m_get();
else
{
if (g_other.m_engaged)
this->m_construct(g_other.m_get());
else
this->m_reset();
}
}
// used to perform non-trivial move assignment.
constexpr void
m_move_assign(G_Optional_payload_base&& g_other)
noexcept(and_<std::is_nothrow_move_constructible<G_Tp>,
std::is_nothrow_move_assignable<G_Tp>>::value)
{
if (this->m_engaged && g_other.m_engaged)
this->m_get() = std::move(g_other.m_get());
else
{
if (g_other.m_engaged)
this->m_construct(std::move(g_other.m_get()));
else
this->m_reset();
}
}
struct G_Empty_byte { };
template<typename G_Up, bool = std::is_trivially_destructible<G_Up>::value>
union G_Storage
{
constexpr G_Storage() noexcept : m_empty() { }
template<typename... G_Args>
constexpr
G_Storage(in_place_t, G_Args&&... g_args)
: m_value(std::forward<G_Args>(g_args)...)
{ }
template<typename G_Vp, typename... G_Args>
constexpr
G_Storage(std::initializer_list<G_Vp> g_il, G_Args&&... g_args)
: m_value(g_il, std::forward<G_Args>(g_args)...)
{ }
template<typename G_Fn, typename G_Arg>
constexpr
G_Storage(G_Optional_func<G_Fn> g_f, G_Arg&& g_arg)
: m_value(gcc::invoke(std::forward<G_Fn>(g_f.m_f),
std::forward<G_Arg>(g_arg)))
{ }
G_Empty_byte m_empty;
G_Up m_value;
};
template<typename G_Up>
union G_Storage<G_Up, false>
{
constexpr G_Storage() noexcept : m_empty() { }
template<typename... G_Args>
constexpr
G_Storage(in_place_t, G_Args&&... g_args)
: m_value(std::forward<G_Args>(g_args)...)
{ }
template<typename G_Vp, typename... G_Args>
constexpr
G_Storage(std::initializer_list<G_Vp> g_il, G_Args&&... g_args)
: m_value(g_il, std::forward<G_Args>(g_args)...)
{ }
template<typename G_Fn, typename G_Arg>
constexpr
G_Storage(G_Optional_func<G_Fn> g_f, G_Arg&& g_arg)
: m_value(gcc::invoke(std::forward<G_Fn>(g_f.m_f),
std::forward<G_Arg>(g_arg)))
{ }
// User-provided destructor is needed when _Up has non-trivial dtor.
GCC_BACKPORT20_CONSTEXPR
~G_Storage() { }
G_Storage(const G_Storage&) = default;
G_Storage(G_Storage&&) = default;
G_Storage& operator=(const G_Storage&) = default;
G_Storage& operator=(G_Storage&&) = default;
G_Empty_byte m_empty;
G_Up m_value;
};
G_Storage<G_Stored_type> m_payload;
bool m_engaged = false;
template<typename... G_Args>
constexpr void
m_construct(G_Args&&... g_args)
noexcept(std::is_nothrow_constructible<G_Stored_type, G_Args...>::value)
{
gcc::construct_at(std::addressof(this->m_payload.m_value),
std::forward<G_Args>(g_args)...);
this->m_engaged = true;
}
constexpr void
m_destroy() noexcept
{
m_engaged = false;
m_payload.m_value.~G_Stored_type();
#if defined(__clang__) && __cplusplus >= 202002L // full constexpr support
if (std::is_constant_evaluated())
// Work around PR124910 for Clang.
gcc::construct_at(std::addressof(m_payload.m_empty));
#endif
}
template<typename G_Fn, typename G_Up>
constexpr void
m_apply(G_Optional_func<G_Fn> g_f, G_Up&& g_x)
{
gcc::construct_at(std::addressof(this->m_payload),
g_f, std::forward<G_Up>(g_x));
m_engaged = true;
}
// The _M_get() operations have _M_engaged as a precondition.
// They exist to access the contained value with the appropriate
// const-qualification, because _M_payload has had the const removed.
constexpr G_Tp&
m_get() noexcept
{ return this->m_payload.m_value; }
constexpr const G_Tp&
m_get() const noexcept
{ return this->m_payload.m_value; }
// _M_reset is a 'safe' operation with no precondition.
constexpr void
m_reset() noexcept
{
if (this->m_engaged)
m_destroy();
else // This seems redundant but improves codegen, see PR 112480.
this->m_engaged = false;
}
};
// Class template that manages the payload for optionals.
template <typename G_Tp,
bool /*_HasTrivialDestructor*/ =
std::is_trivially_destructible<G_Tp>::value,
bool /*_HasTrivialCopy */ =
std::is_trivially_copy_assignable<G_Tp>::value
&& std::is_trivially_copy_constructible<G_Tp>::value,
bool /*_HasTrivialMove */ =
std::is_trivially_move_assignable<G_Tp>::value
&& std::is_trivially_move_constructible<G_Tp>::value>
struct G_Optional_payload;
// Payload for potentially-constexpr optionals (trivial copy/move/destroy).
template <typename G_Tp>
struct G_Optional_payload<G_Tp, true, true, true>
: G_Optional_payload_base<G_Tp>
{
using G_Optional_payload_base<G_Tp>::G_Optional_payload_base;
G_Optional_payload() = default;
};
// Payload for optionals with non-trivial copy construction/assignment.
template <typename G_Tp>
struct G_Optional_payload<G_Tp, true, false, true>
: G_Optional_payload_base<G_Tp>
{
using G_Optional_payload_base<G_Tp>::G_Optional_payload_base;
G_Optional_payload() = default;
~G_Optional_payload() = default;
G_Optional_payload(const G_Optional_payload&) = default;
G_Optional_payload(G_Optional_payload&&) = default;
G_Optional_payload& operator=(G_Optional_payload&&) = default;
// Non-trivial copy assignment.
constexpr
G_Optional_payload&
operator=(const G_Optional_payload& g_other)
{
this->m_copy_assign(g_other);
return *this;
}
};
// Payload for optionals with non-trivial move construction/assignment.
template <typename G_Tp>
struct G_Optional_payload<G_Tp, true, true, false>
: G_Optional_payload_base<G_Tp>
{
using G_Optional_payload_base<G_Tp>::G_Optional_payload_base;
G_Optional_payload() = default;
~G_Optional_payload() = default;
G_Optional_payload(const G_Optional_payload&) = default;
G_Optional_payload(G_Optional_payload&&) = default;
G_Optional_payload& operator=(const G_Optional_payload&) = default;
// Non-trivial move assignment.
constexpr
G_Optional_payload&
operator=(G_Optional_payload&& g_other)
noexcept(and_<std::is_nothrow_move_constructible<G_Tp>,
std::is_nothrow_move_assignable<G_Tp>>::value)
{
this->m_move_assign(std::move(g_other));
return *this;
}
};
// Payload for optionals with non-trivial copy and move assignment.
template <typename G_Tp>
struct G_Optional_payload<G_Tp, true, false, false>
: G_Optional_payload_base<G_Tp>
{
using G_Optional_payload_base<G_Tp>::G_Optional_payload_base;
G_Optional_payload() = default;
~G_Optional_payload() = default;
G_Optional_payload(const G_Optional_payload&) = default;
G_Optional_payload(G_Optional_payload&&) = default;
// Non-trivial copy assignment.
constexpr
G_Optional_payload&
operator=(const G_Optional_payload& g_other)
{
this->m_copy_assign(g_other);
return *this;
}
// Non-trivial move assignment.
constexpr
G_Optional_payload&
operator=(G_Optional_payload&& g_other)
noexcept(and_<std::is_nothrow_move_constructible<G_Tp>,
std::is_nothrow_move_assignable<G_Tp>>::value)
{
this->m_move_assign(std::move(g_other));
return *this;
}
};
// Payload for optionals with non-trivial destructors.
template <typename G_Tp, bool G_Copy, bool G_Move>
struct G_Optional_payload<G_Tp, false, G_Copy, G_Move>
: G_Optional_payload<G_Tp, true, false, false>
{
// Base class implements all the constructors and assignment operators:
using G_Optional_payload<G_Tp, true, false, false>::G_Optional_payload;
G_Optional_payload() = default;
G_Optional_payload(const G_Optional_payload&) = default;
G_Optional_payload(G_Optional_payload&&) = default;
G_Optional_payload& operator=(const G_Optional_payload&) = default;
G_Optional_payload& operator=(G_Optional_payload&&) = default;
// Destructor needs to destroy the contained value:
GCC_BACKPORT20_CONSTEXPR ~G_Optional_payload() { this->m_reset(); }
};
/**
* @brief Class template that provides copy/move constructors of optional.
*
* Such a separate base class template is necessary in order to
* conditionally make copy/move constructors trivial.
*
* When the contained value is trivially copy/move constructible,
* the copy/move constructors of _Optional_base will invoke the
* trivial copy/move constructor of _Optional_payload. Otherwise,
* they will invoke _Optional_payload(bool, const _Optional_payload&)
* or _Optional_payload(bool, _Optional_payload&&) to initialize
* the contained value, if copying/moving an engaged optional.
*
* Whether the other special members are trivial is determined by the
* _Optional_payload<_Tp> specialization used for the _M_payload member.
*
* @see optional, _Enable_special_members
*/
template<typename G_Tp,
bool = std::is_trivially_copy_constructible<G_Tp>::value,
bool = std::is_trivially_move_constructible<G_Tp>::value>
struct G_Optional_base
{
// Constructors for disengaged optionals.
constexpr G_Optional_base() = default;
// Constructors for engaged optionals.
template<typename... G_Args,
std::enable_if_t<std::is_constructible<G_Tp, G_Args...>::value, bool> = false>
constexpr explicit
G_Optional_base(in_place_t, G_Args&&... g_args)
: m_payload(in_place, std::forward<G_Args>(g_args)...)
{ }
template<typename G_Up, typename... G_Args,
std::enable_if_t<std::is_constructible<G_Tp,
std::initializer_list<G_Up>&,
G_Args...>::value, bool> = false>
constexpr explicit
G_Optional_base(in_place_t,
std::initializer_list<G_Up> g_il,
G_Args&&... g_args)
: m_payload(in_place, g_il, std::forward<G_Args>(g_args)...)
{ }
// Copy and move constructors.
constexpr
G_Optional_base(const G_Optional_base& g_other)
noexcept(std::is_nothrow_copy_constructible<G_Tp>::value)
: m_payload(g_other.m_payload.m_engaged, g_other.m_payload)
{ }
constexpr
G_Optional_base(G_Optional_base&& g_other)
noexcept(std::is_nothrow_move_constructible<G_Tp>::value)
: m_payload(g_other.m_payload.m_engaged,
std::move(g_other.m_payload))
{ }
// Assignment operators.
G_Optional_base& operator=(const G_Optional_base&) = default;
G_Optional_base& operator=(G_Optional_base&&) = default;
G_Optional_payload<G_Tp> m_payload;
protected:
// For the primary template, we define these functions here.
using G_Stored_type = std::remove_const_t<G_Tp>;
// The _M_construct operation has !_M_engaged as a precondition
// while _M_destruct has _M_engaged as a precondition.
template<typename... G_Args>
constexpr void
m_construct(G_Args&&... g_args)
noexcept(std::is_nothrow_constructible<G_Stored_type, G_Args...>::value)
{
m_payload.m_construct(std::forward<G_Args>(g_args)...);
}
constexpr void
m_destruct() noexcept
{ m_payload.m_destroy(); }
// _M_reset is a 'safe' operation with no precondition.
constexpr void
m_reset() noexcept
{ m_payload.m_reset(); }
constexpr bool m_is_engaged() const noexcept
{ return m_payload.m_engaged; }
// The _M_get operations have _M_engaged as a precondition.
constexpr G_Tp&
m_get() noexcept
{ return m_payload.m_get(); }
constexpr const G_Tp&
m_get() const noexcept
{ return m_payload.m_get(); }
};
// If P0848R3 "Conditionally Trivial Special Member Functions" is not
// supported (as determined from the __cpp_concepts macro value), the
// _Optional_base primary template only has non-trivial copy and move
// constructors. Use partial specializations of _Optional_base<T, C, M>
// that have a trivial copy and/or move constructor.
// Common base class for _Optional_base<T> to avoid repeating these
// member functions in each partial specialization.
// Only used if P0848R3 "Conditionally Trivial Special Member Functions"
// is not supported, as indicated by the __cpp_concepts value.
template<typename G_Tp, typename G_Dp>
class G_Optional_base_impl
{
protected:
using G_Stored_type = std::remove_const_t<G_Tp>;
// The _M_construct operation has !_M_engaged as a precondition
// while _M_destruct has _M_engaged as a precondition.
template<typename... G_Args>
constexpr void
m_construct(G_Args&&... g_args)
noexcept(std::is_nothrow_constructible<G_Stored_type, G_Args...>::value)
{
static_cast<G_Dp*>(this)->m_payload.m_construct(
std::forward<G_Args>(g_args)...);
}
constexpr void
m_destruct() noexcept
{ static_cast<G_Dp*>(this)->m_payload.m_destroy(); }
// _M_reset is a 'safe' operation with no precondition.
constexpr void
m_reset() noexcept
{ static_cast<G_Dp*>(this)->m_payload.m_reset(); }
constexpr bool m_is_engaged() const noexcept
{ return static_cast<const G_Dp*>(this)->m_payload.m_engaged; }
// The _M_get operations have _M_engaged as a precondition.
constexpr G_Tp&
m_get() noexcept
{ return static_cast<G_Dp*>(this)->m_payload.m_get(); }
constexpr const G_Tp&
m_get() const noexcept
{ return static_cast<const G_Dp*>(this)->m_payload.m_get(); }
};
template<typename G_Tp>
struct G_Optional_base<G_Tp, false, true> // trivial move ctor
: G_Optional_base_impl<G_Tp, G_Optional_base<G_Tp>>
{
// Constructors for disengaged optionals.
constexpr G_Optional_base() = default;
// Constructors for engaged optionals.
template<typename... G_Args,
std::enable_if_t<std::is_constructible<G_Tp, G_Args...>::value, bool> = false>
constexpr explicit
G_Optional_base(in_place_t, G_Args&&... g_args)
: m_payload(in_place, std::forward<G_Args>(g_args)...)
{ }
template<typename G_Up, typename... G_Args,
std::enable_if_t<std::is_constructible<G_Tp,
std::initializer_list<G_Up>&,
G_Args...>::value, bool> = false>
constexpr explicit
G_Optional_base(in_place_t,
std::initializer_list<G_Up> g_il,
G_Args... g_args)
: m_payload(in_place, g_il, std::forward<G_Args>(g_args)...)
{ }
// Copy and move constructors.
constexpr G_Optional_base(const G_Optional_base& g_other)
: m_payload(g_other.m_payload.m_engaged, g_other.m_payload)
{ }
constexpr G_Optional_base(G_Optional_base&& g_other) = default;
// Assignment operators.
G_Optional_base& operator=(const G_Optional_base&) = default;
G_Optional_base& operator=(G_Optional_base&&) = default;
G_Optional_payload<G_Tp> m_payload;
};
template<typename G_Tp>
struct G_Optional_base<G_Tp, true, false> // trivial copy ctor
: G_Optional_base_impl<G_Tp, G_Optional_base<G_Tp>>
{
// Constructors for disengaged optionals.
constexpr G_Optional_base() = default;
// Constructors for engaged optionals.
template<typename... G_Args,
std::enable_if_t<std::is_constructible<G_Tp, G_Args...>::value, bool> = false>
constexpr explicit
G_Optional_base(in_place_t, G_Args&&... g_args)
: m_payload(in_place, std::forward<G_Args>(g_args)...)
{ }
template<typename G_Up, typename... G_Args,
std::enable_if_t<std::is_constructible<G_Tp,
std::initializer_list<G_Up>&,
G_Args...>::value, bool> = false>
constexpr explicit
G_Optional_base(in_place_t,
std::initializer_list<G_Up> g_il,
G_Args&&... g_args)
: m_payload(in_place, g_il, std::forward<G_Args>(g_args)...)
{ }
// Copy and move constructors.
constexpr G_Optional_base(const G_Optional_base& g_other) = default;
constexpr
G_Optional_base(G_Optional_base&& g_other)
noexcept(std::is_nothrow_move_constructible<G_Tp>::value)
: m_payload(g_other.m_payload.m_engaged,
std::move(g_other.m_payload))
{ }
// Assignment operators.
G_Optional_base& operator=(const G_Optional_base&) = default;
G_Optional_base& operator=(G_Optional_base&&) = default;
G_Optional_payload<G_Tp> m_payload;
};
template<typename G_Tp>
struct G_Optional_base<G_Tp, true, true> // trivial copy and move ctors
: G_Optional_base_impl<G_Tp, G_Optional_base<G_Tp>>
{
// Constructors for disengaged optionals.
constexpr G_Optional_base() = default;
// Constructors for engaged optionals.
template<typename... G_Args,
std::enable_if_t<std::is_constructible<G_Tp, G_Args...>::value, bool> = false>
constexpr explicit
G_Optional_base(in_place_t, G_Args&&... g_args)
: m_payload(in_place, std::forward<G_Args>(g_args)...)
{ }
template<typename G_Up, typename... G_Args,
std::enable_if_t<std::is_constructible<G_Tp,
std::initializer_list<G_Up>&,
G_Args...>::value, bool> = false>
constexpr explicit
G_Optional_base(in_place_t,
std::initializer_list<G_Up> g_il,
G_Args&&... g_args)
: m_payload(in_place, g_il, std::forward<G_Args>(g_args)...)
{ }
// Copy and move constructors.
constexpr G_Optional_base(const G_Optional_base& g_other) = default;
constexpr G_Optional_base(G_Optional_base&& g_other) = default;
// Assignment operators.
G_Optional_base& operator=(const G_Optional_base&) = default;
G_Optional_base& operator=(G_Optional_base&&) = default;
G_Optional_payload<G_Tp> m_payload;
};
template<typename>
struct g_is_optional : std::false_type {};
template<typename G_Tp>
struct g_is_optional<optional<G_Tp>> : std::true_type {};
template<typename G_Tp, typename G_Up>
using g_converts_from_optional
= g_converts_from_any_cvref<G_Tp, optional<G_Up>>;
template<typename G_Tp, typename G_Up>
using g_assigns_from_optional =
or_<std::is_assignable<G_Tp&, const optional<G_Up>&>,
std::is_assignable<G_Tp&, optional<G_Up>&>,
std::is_assignable<G_Tp&, const optional<G_Up>&&>,
std::is_assignable<G_Tp&, optional<G_Up>&&>>;
/**
* @brief Class template for optional values.
*/
template<typename G_Tp>
class optional
: private G_Optional_base<G_Tp>,
private G_Enable_copy_move<
// Copy constructor.
std::is_copy_constructible<G_Tp>::value,
// Copy assignment.
and_<std::is_copy_constructible<G_Tp>, std::is_copy_assignable<G_Tp>>::value,
// Move constructor.
std::is_move_constructible<G_Tp>::value,
// Move assignment.
and_<std::is_move_constructible<G_Tp>, std::is_move_assignable<G_Tp>>::value,
// Unique tag type.
optional<G_Tp>>
{
static_assert (g_is_valid_contained_type_for_optional<G_Tp>,
"Given type not valid for an optional");
private:
using G_Base = G_Optional_base<G_Tp>;
// SFINAE helpers
template<typename G_From, typename = std::remove_cv_t<G_Tp>>
struct g_not_constructing_bool_from_optional
: std::true_type
{ };
template<typename G_From>
struct g_not_constructing_bool_from_optional<G_From, bool>
: not_<g_is_optional<remove_cvref_t<G_From>>>
{ };
template<typename G_From, typename = std::remove_cv_t<G_Tp>>
struct g_construct_from_contained_value
: not_<g_converts_from_optional<G_Tp, G_From>>
{ };
template<typename G_From>
struct g_construct_from_contained_value<G_From, bool>
: std::true_type
{ };
template<typename G_Up>
using g_not_self = not_<std::is_same<optional, remove_cvref_t<G_Up>>>;
template<typename G_Up>
using g_not_tag = not_<std::is_same<in_place_t, remove_cvref_t<G_Up>>>;
public:
using value_type = G_Tp;
constexpr optional() noexcept { }
constexpr optional(nullopt_t) noexcept { }
template<typename G_Up = std::remove_cv_t<G_Tp>,
G_Requires<g_not_self<G_Up>, g_not_tag<G_Up>,
std::is_constructible<G_Tp, G_Up>,
std::is_convertible<G_Up, G_Tp>,
g_not_constructing_bool_from_optional<G_Up>> = true>
constexpr
optional(G_Up&& g_t)
noexcept(std::is_nothrow_constructible<G_Tp, G_Up>::value)
: G_Base(in_place, std::forward<G_Up>(g_t)) { }
template<typename G_Up = std::remove_cv_t<G_Tp>,
G_Requires<g_not_self<G_Up>, g_not_tag<G_Up>,
std::is_constructible<G_Tp, G_Up>,
not_<std::is_convertible<G_Up, G_Tp>>,
g_not_constructing_bool_from_optional<G_Up>> = false>
explicit constexpr
optional(G_Up&& g_t)
noexcept(std::is_nothrow_constructible<G_Tp, G_Up>::value)
: G_Base(in_place, std::forward<G_Up>(g_t)) { }
template<typename G_Up,
G_Requires<not_<std::is_same<G_Tp, G_Up>>,
std::is_constructible<G_Tp, const G_Up&>,
std::is_convertible<const G_Up&, G_Tp>,
g_construct_from_contained_value<G_Up>> = true>
constexpr
optional(const optional<G_Up>& g_t)
noexcept(std::is_nothrow_constructible<G_Tp, const G_Up&>::value)
{
if (g_t)
emplace(g_t.m_fwd());
}
template<typename G_Up,
G_Requires<not_<std::is_same<G_Tp, G_Up>>,
std::is_constructible<G_Tp, const G_Up&>,
not_<std::is_convertible<const G_Up&, G_Tp>>,
g_construct_from_contained_value<G_Up>> = false>
explicit constexpr
optional(const optional<G_Up>& g_t)
noexcept(std::is_nothrow_constructible<G_Tp, const G_Up&>::value)
{
if (g_t)
emplace(g_t.m_fwd());
}
template<typename G_Up,
G_Requires<not_<std::is_same<G_Tp, G_Up>>,
std::is_constructible<G_Tp, G_Up>,
std::is_convertible<G_Up, G_Tp>,
g_construct_from_contained_value<G_Up>> = true>
constexpr
optional(optional<G_Up>&& g_t)
noexcept(std::is_nothrow_constructible<G_Tp, G_Up>::value)
{
if (g_t)
emplace(std::move(g_t).m_fwd());
}
template<typename G_Up,
G_Requires<not_<std::is_same<G_Tp, G_Up>>,
std::is_constructible<G_Tp, G_Up>,
not_<std::is_convertible<G_Up, G_Tp>>,
g_construct_from_contained_value<G_Up>> = false>
explicit constexpr
optional(optional<G_Up>&& g_t)
noexcept(std::is_nothrow_constructible<G_Tp, G_Up>::value)
{
if (g_t)
emplace(std::move(g_t).m_fwd());
}
template<typename... G_Args,
G_Requires<std::is_constructible<G_Tp, G_Args...>> = false>
explicit constexpr
optional(in_place_t, G_Args&&... g_args)
noexcept(std::is_nothrow_constructible<G_Tp, G_Args...>::value)
: G_Base(in_place, std::forward<G_Args>(g_args)...) { }
template<typename G_Up, typename... G_Args,
G_Requires<std::is_constructible<G_Tp,
std::initializer_list<G_Up>&,
G_Args...>> = false>
explicit constexpr
optional(in_place_t, std::initializer_list<G_Up> g_il, G_Args&&... g_args)
noexcept(std::is_nothrow_constructible<G_Tp, std::initializer_list<G_Up>&,
G_Args...>::value)
: G_Base(in_place, g_il, std::forward<G_Args>(g_args)...) { }
// Assignment operators.
constexpr optional&
operator=(nullopt_t) noexcept
{
this->m_reset();
return *this;
}
template<typename G_Up = std::remove_cv_t<G_Tp>>
constexpr
std::enable_if_t<and_<g_not_self<G_Up>,
not_<and_<std::is_scalar<G_Tp>,
std::is_same<G_Tp, std::decay_t<G_Up>>>>,
std::is_constructible<G_Tp, G_Up>,
std::is_assignable<G_Tp&, G_Up>>::value,
optional&>
operator=(G_Up&& g_u)
noexcept(and_<std::is_nothrow_constructible<G_Tp, G_Up>,
std::is_nothrow_assignable<G_Tp&, G_Up>>::value)
{
if (this->m_is_engaged())
this->m_get() = std::forward<G_Up>(g_u);
else
this->m_construct(std::forward<G_Up>(g_u));
return *this;
}
template<typename G_Up>
constexpr
std::enable_if_t<and_<not_<std::is_same<G_Tp, G_Up>>,
std::is_constructible<G_Tp, const G_Up&>,
std::is_assignable<G_Tp&, const G_Up&>,
not_<g_converts_from_optional<G_Tp, G_Up>>,
not_<g_assigns_from_optional<G_Tp, G_Up>>>::value,
optional&>
operator=(const optional<G_Up>& g_u)
noexcept(and_<std::is_nothrow_constructible<G_Tp, const G_Up&>,
std::is_nothrow_assignable<G_Tp&, const G_Up&>>::value)
{
if (g_u)
{
if (this->m_is_engaged())
this->m_get() = g_u.m_fwd();
else
this->m_construct(g_u.m_fwd());
}
else
{
this->m_reset();
}
return *this;
}
template<typename G_Up>
constexpr
std::enable_if_t<and_<not_<std::is_same<G_Tp, G_Up>>,
std::is_constructible<G_Tp, G_Up>,
std::is_assignable<G_Tp&, G_Up>,
not_<g_converts_from_optional<G_Tp, G_Up>>,
not_<g_assigns_from_optional<G_Tp, G_Up>>>::value,
optional&>
operator=(optional<G_Up>&& g_u)
noexcept(and_<std::is_nothrow_constructible<G_Tp, G_Up>,
std::is_nothrow_assignable<G_Tp&, G_Up>>::value)
{
if (g_u)
{
if (this->m_is_engaged())
this->m_get() = std::move(g_u).m_fwd();
else
this->m_construct(std::move(g_u).m_fwd());
}
else
{
this->m_reset();
}
return *this;
}
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2746. Inconsistency between requirements for emplace between optional and variant
template<typename... G_Args>
constexpr
std::enable_if_t<std::is_constructible<G_Tp, G_Args...>::value, G_Tp&>
emplace(G_Args&&... g_args)
noexcept(std::is_nothrow_constructible<G_Tp, G_Args...>::value)
{
this->m_reset();
this->m_construct(std::forward<G_Args>(g_args)...);
return this->m_get();
}
template<typename G_Up, typename... G_Args>
constexpr
std::enable_if_t<std::is_constructible<G_Tp, std::initializer_list<G_Up>&, G_Args...>::value,
G_Tp&>
emplace(std::initializer_list<G_Up> g_il, G_Args&&... g_args)
noexcept(std::is_nothrow_constructible<G_Tp, std::initializer_list<G_Up>&,
G_Args...>::value)
{
this->m_reset();
this->m_construct(g_il, std::forward<G_Args>(g_args)...);
return this->m_get();
}
// Destructor is implicit, implemented in _Optional_base.
// Swap.
constexpr void
swap(optional& g_other)
noexcept(std::is_nothrow_move_constructible<G_Tp>::value
&& g_is_nothrow_swappable<G_Tp>::value)
{
using std::swap;
if (this->m_is_engaged() && g_other.m_is_engaged())
swap(this->m_get(), g_other.m_get());
else if (this->m_is_engaged())
{
g_other.m_construct(std::move(this->m_get()));
this->m_destruct();
}
else if (g_other.m_is_engaged())
{
this->m_construct(std::move(g_other.m_get()));
g_other.m_destruct();
}
}
// Observers.
constexpr const G_Tp*
operator->() const noexcept
{
gccbackport_assert(this->m_is_engaged());
return std::addressof(this->m_get());
}
constexpr G_Tp*
operator->() noexcept
{
gccbackport_assert(this->m_is_engaged());
return std::addressof(this->m_get());
}
constexpr const G_Tp&
operator*() const& noexcept
{
gccbackport_assert(this->m_is_engaged());
return this->m_get();
}
constexpr G_Tp&
operator*()& noexcept
{
gccbackport_assert(this->m_is_engaged());
return this->m_get();
}
constexpr G_Tp&&
operator*()&& noexcept
{
gccbackport_assert(this->m_is_engaged());
return std::move(this->m_get());
}
constexpr const G_Tp&&
operator*() const&& noexcept
{
gccbackport_assert(this->m_is_engaged());
return std::move(this->m_get());
}
constexpr explicit operator bool() const noexcept
{ return this->m_is_engaged(); }
constexpr bool has_value() const noexcept
{ return this->m_is_engaged(); }
constexpr const G_Tp&
value() const&
{
if (this->m_is_engaged())
return this->m_get();
g_throw_bad_optional_access();
}
constexpr G_Tp&
value()&
{
if (this->m_is_engaged())
return this->m_get();
g_throw_bad_optional_access();
}
constexpr G_Tp&&
value()&&
{
if (this->m_is_engaged())
return std::move(this->m_get());
g_throw_bad_optional_access();
}
constexpr const G_Tp&&
value() const&&
{
if (this->m_is_engaged())
return std::move(this->m_get());
g_throw_bad_optional_access();
}
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 4406. value_or return statement is inconsistent with Mandates
template<typename G_Up = std::remove_cv_t<G_Tp>>
constexpr std::remove_cv_t<G_Tp>
value_or(G_Up&& g_u) const&
{
using G_Xp = std::remove_cv_t<G_Tp>;
static_assert(std::is_convertible<const G_Tp&, G_Xp>::value, "");
static_assert(std::is_convertible<G_Up, G_Xp>::value, "");
if (this->m_is_engaged())
return this->m_get();
return std::forward<G_Up>(g_u);
}
template<typename G_Up = std::remove_cv_t<G_Tp>>
constexpr std::remove_cv_t<G_Tp>
value_or(G_Up&& g_u) &&
{
using G_Xp = std::remove_cv_t<G_Tp>;
static_assert(std::is_convertible<G_Tp, G_Xp>::value, "");
static_assert(std::is_convertible<G_Up, G_Xp>::value, "");
if (this->m_is_engaged())
return std::move(this->m_get());
return std::forward<G_Up>(g_u);
}
// [optional.monadic]
template<typename G_Fn>
constexpr auto
and_then(G_Fn&& g_f) &
{
using G_Up = remove_cvref_t<g_invoke_result_t<G_Fn, G_Tp&>>;
static_assert(g_is_optional<G_Up>::value,
"the function passed to std::optional<T>::and_then "
"must return a std::optional");
if (has_value())
return gcc::invoke(std::forward<G_Fn>(g_f), m_get());
else
return G_Up();
}
template<typename G_Fn>
constexpr auto
and_then(G_Fn&& g_f) const &
{
using G_Up = remove_cvref_t<g_invoke_result_t<G_Fn, const G_Tp&>>;
static_assert(g_is_optional<G_Up>::value,
"the function passed to std::optional<T>::and_then "
"must return a std::optional");
if (has_value())
return gcc::invoke(std::forward<G_Fn>(g_f), m_get());
else
return G_Up();
}
template<typename G_Fn>
constexpr auto
and_then(G_Fn&& g_f) &&
{
using G_Up = remove_cvref_t<g_invoke_result_t<G_Fn, G_Tp>>;
static_assert(g_is_optional<G_Up>::value,
"the function passed to std::optional<T>::and_then "
"must return a std::optional");
if (has_value())
return gcc::invoke(std::forward<G_Fn>(g_f), std::move(m_get()));
else
return G_Up();
}
template<typename G_Fn>
constexpr auto
and_then(G_Fn&& g_f) const &&
{
using G_Up = remove_cvref_t<g_invoke_result_t<G_Fn, const G_Tp>>;
static_assert(g_is_optional<G_Up>::value,
"the function passed to std::optional<T>::and_then "
"must return a std::optional");
if (has_value())
return gcc::invoke(std::forward<G_Fn>(g_f), std::move(m_get()));
else
return G_Up();
}
template<typename G_Fn>
constexpr auto
transform(G_Fn&& g_f) &
{
using G_Up = std::remove_cv_t<g_invoke_result_t<G_Fn, G_Tp&>>;
if (has_value())
return optional<G_Up>(G_Optional_func<G_Fn>{g_f}, m_get());
else
return optional<G_Up>();
}
template<typename G_Fn>
constexpr auto
transform(G_Fn&& g_f) const &
{
using G_Up = std::remove_cv_t<g_invoke_result_t<G_Fn, const G_Tp&>>;
if (has_value())
return optional<G_Up>(G_Optional_func<G_Fn>{g_f}, m_get());
else
return optional<G_Up>();
}
template<typename G_Fn>
constexpr auto
transform(G_Fn&& g_f) &&
{
using G_Up = std::remove_cv_t<g_invoke_result_t<G_Fn, G_Tp>>;
if (has_value())
return optional<G_Up>(G_Optional_func<G_Fn>{g_f}, std::move(m_get()));
else
return optional<G_Up>();
}
template<typename G_Fn>
constexpr auto
transform(G_Fn&& g_f) const &&
{
using G_Up = std::remove_cv_t<g_invoke_result_t<G_Fn, const G_Tp>>;
if (has_value())
return optional<G_Up>(G_Optional_func<G_Fn>{g_f}, std::move(m_get()));
else
return optional<G_Up>();
}
template<typename G_Fn,
G_Requires<
gcc::g_is_invocable<G_Fn>,
std::is_copy_constructible<G_Tp>
> = false>
constexpr optional
or_else(G_Fn&& g_f) const&
{
using G_Up = g_invoke_result_t<G_Fn>;
static_assert(std::is_same<remove_cvref_t<G_Up>, optional>::value,
"the function passed to std::optional<T>::or_else "
"must return a std::optional<T>");
if (has_value())
return *this;
else
return std::forward<G_Fn>(g_f)();
}
template<typename G_Fn,
G_Requires<
gcc::g_is_invocable<G_Fn>,
std::is_move_constructible<G_Tp>
> = false>
constexpr optional
or_else(G_Fn&& g_f) &&
{
using G_Up = g_invoke_result_t<G_Fn>;
static_assert(std::is_same<remove_cvref_t<G_Up>, optional>::value,
"the function passed to std::optional<T>::or_else "
"must return a std::optional<T>");
if (has_value())
return std::move(*this);
else
return std::forward<G_Fn>(g_f)();
}
constexpr void reset() noexcept { this->m_reset(); }
private:
using G_Base::m_get;
[[gnu::always_inline]]
constexpr G_Tp&
m_fwd() & noexcept
{ return m_get(); }
[[gnu::always_inline]]
constexpr G_Tp&&
m_fwd() && noexcept
{ return std::move(m_get()); }
[[gnu::always_inline]]
constexpr const G_Tp&
m_fwd() const& noexcept
{ return m_get(); }
[[gnu::always_inline]]
constexpr const G_Tp&&
m_fwd() const&& noexcept
{ return std::move(m_get()); }
template<typename G_Up> friend class optional;
template<typename G_Fn, typename G_Value>
explicit constexpr
optional(G_Optional_func<G_Fn> g_f, G_Value&& g_v)
{
this->m_payload.m_apply(g_f, std::forward<G_Value>(g_v));
}
};
template<typename G_Tp>
constexpr G_Tp&
optional<G_Tp&>::value() const
{
if (m_val)
return *m_val;
g_throw_bad_optional_access();
}
template<typename G_Tp>
template<typename G_Up,
typename G_Tp_,
G_Requires<
std::is_object<G_Tp_>,
not_<std::is_array<G_Tp_>>
>>
constexpr std::decay_t<G_Tp>
optional<G_Tp&>::value_or(G_Up&& g_u) const
{
using G_Xp = std::remove_cv_t<G_Tp>;
static_assert(std::is_convertible<G_Tp&, G_Xp>::value, "");
static_assert(std::is_convertible<G_Up, G_Xp>::value, "");
if (m_val)
return *m_val;
return std::forward<G_Up>(g_u);
}
template<typename G_Tp>
template<typename G_Fn>
constexpr auto
optional<G_Tp&>::and_then(G_Fn&& g_f) const
{
using G_Up = remove_cvref_t<g_invoke_result_t<G_Fn, G_Tp&>>;
static_assert(g_is_optional<G_Up>::value,
"the function passed to std::optional<T&>::and_then "
"must return a std::optional");
if (has_value())
return gcc::invoke(std::forward<G_Fn>(g_f), *m_val);
else
return G_Up();
}
template<typename G_Tp>
template<typename G_Fn>
constexpr optional<std::remove_cv_t<g_invoke_result_t<G_Fn, G_Tp&>>>
optional<G_Tp&>::transform(G_Fn&& g_f) const
{
using G_Up = std::remove_cv_t<g_invoke_result_t<G_Fn, G_Tp&>>;
if (has_value())
return optional<G_Up>(G_Optional_func<G_Fn>{g_f}, *m_val);
else
return optional<G_Up>();
}
template<typename G_Tp>
template<typename G_Fn,
G_Requires<g_is_invocable<G_Fn>>>
constexpr optional<G_Tp&>
optional<G_Tp&>::or_else(G_Fn&& g_f) const
{
static_assert(std::is_same<remove_cvref_t<g_invoke_result_t<G_Fn>>, optional>::value,
"the function passed to std::optional<T&>::or_else "
"must return a std::optional<T&>");
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 4367. Improve optional<T&>::or_else
if (has_value())
return *this;
else
return std::forward<G_Fn>(g_f)();
}
template<typename G_Tp>
template<typename G_Fn, typename G_Value>
constexpr
optional<G_Tp&>::optional(G_Optional_func<G_Fn> g_f, G_Value&& g_v)
{
G_Tp& g_r = gcc::invoke(std::forward<G_Fn>(g_f.m_f), std::forward<G_Value>(g_v));
m_val = std::addressof(g_r);
}
template<typename G_Tp>
using g_optional_relop_t =
std::enable_if_t<std::is_convertible<G_Tp, bool>::value, bool>;
template<typename G_Tp, typename G_Up>
using g_optional_eq_t = g_optional_relop_t<
decltype(std::declval<const G_Tp&>() == std::declval<const G_Up&>())
>;
template<typename G_Tp, typename G_Up>
using g_optional_ne_t = g_optional_relop_t<
decltype(std::declval<const G_Tp&>() != std::declval<const G_Up&>())
>;
template<typename G_Tp, typename G_Up>
using g_optional_lt_t = g_optional_relop_t<
decltype(std::declval<const G_Tp&>() < std::declval<const G_Up&>())
>;
template<typename G_Tp, typename G_Up>
using g_optional_gt_t = g_optional_relop_t<
decltype(std::declval<const G_Tp&>() > std::declval<const G_Up&>())
>;
template<typename G_Tp, typename G_Up>
using g_optional_le_t = g_optional_relop_t<
decltype(std::declval<const G_Tp&>() <= std::declval<const G_Up&>())
>;
template<typename G_Tp, typename G_Up>
using g_optional_ge_t = g_optional_relop_t<
decltype(std::declval<const G_Tp&>() >= std::declval<const G_Up&>())
>;
// Comparisons between optional values.
template<typename G_Tp, typename G_Up>
constexpr auto
operator==(const optional<G_Tp>& g_lhs, const optional<G_Up>& g_rhs)
-> g_optional_eq_t<G_Tp, G_Up>
{
if (g_lhs.has_value() != g_rhs.has_value())
return false;
if (!g_lhs.has_value())
return true;
return *g_lhs == *g_rhs;
}
template<typename G_Tp, typename G_Up>
constexpr auto
operator!=(const optional<G_Tp>& g_lhs, const optional<G_Up>& g_rhs)
-> g_optional_ne_t<G_Tp, G_Up>
{
if (g_lhs.has_value() != g_rhs.has_value())
return true;
if (!g_lhs.has_value())
return false;
return *g_lhs != *g_rhs;
}
template<typename G_Tp, typename G_Up>
constexpr auto
operator<(const optional<G_Tp>& g_lhs, const optional<G_Up>& g_rhs)
-> g_optional_lt_t<G_Tp, G_Up>
{
if (!g_rhs.has_value())
return false;
if (!g_lhs.has_value())
return true;
return *g_lhs < *g_rhs;
}
template<typename G_Tp, typename G_Up>
constexpr auto
operator>(const optional<G_Tp>& g_lhs, const optional<G_Up>& g_rhs)
-> g_optional_gt_t<G_Tp, G_Up>
{
if (!g_lhs.has_value())
return false;
if (!g_rhs.has_value())
return true;
return *g_lhs > *g_rhs;
}
template<typename G_Tp, typename G_Up>
constexpr auto
operator<=(const optional<G_Tp>& g_lhs, const optional<G_Up>& g_rhs)
-> g_optional_le_t<G_Tp, G_Up>
{
if (!g_lhs.has_value())
return true;
if (!g_rhs.has_value())
return false;
return *g_lhs <= *g_rhs;
}
template<typename G_Tp, typename G_Up>
constexpr auto
operator>=(const optional<G_Tp>& g_lhs, const optional<G_Up>& g_rhs)
-> g_optional_ge_t<G_Tp, G_Up>
{
if (!g_rhs.has_value())
return true;
if (!g_lhs.has_value())
return false;
return *g_lhs >= *g_rhs;
}
#ifdef __cpp_lib_three_way_comparison
template<typename G_Tp, std::three_way_comparable_with<G_Tp> G_Up>
[[nodiscard]]
constexpr std::compare_three_way_result_t<G_Tp, G_Up>
operator<=>(const optional<G_Tp>& g_x, const optional<G_Up>& g_y)
{
return g_x && g_y ? *g_x <=> *g_y : bool(g_x) <=> bool(g_y);
}
#endif
// Comparisons with nullopt.
template<typename G_Tp>
[[nodiscard]]
constexpr bool
operator==(const optional<G_Tp>& g_lhs, nullopt_t) noexcept
{ return !g_lhs; }
#ifdef __cpp_lib_three_way_comparison
template<typename G_Tp>
[[nodiscard]]
constexpr std::strong_ordering
operator<=>(const optional<G_Tp>& g_x, nullopt_t) noexcept
{ return bool(g_x) <=> false; }
#else
template<typename G_Tp>
constexpr bool
operator==(nullopt_t, const optional<G_Tp>& g_rhs) noexcept
{ return !g_rhs; }
template<typename G_Tp>
constexpr bool
operator!=(const optional<G_Tp>& g_lhs, nullopt_t) noexcept
{ return static_cast<bool>(g_lhs); }
template<typename G_Tp>
constexpr bool
operator!=(nullopt_t, const optional<G_Tp>& g_rhs) noexcept
{ return static_cast<bool>(g_rhs); }
template<typename G_Tp>
constexpr bool
operator<(const optional<G_Tp>& /* __lhs */, nullopt_t) noexcept
{ return false; }
template<typename G_Tp>
constexpr bool
operator<(nullopt_t, const optional<G_Tp>& g_rhs) noexcept
{ return static_cast<bool>(g_rhs); }
template<typename G_Tp>
constexpr bool
operator>(const optional<G_Tp>& g_lhs, nullopt_t) noexcept
{ return static_cast<bool>(g_lhs); }
template<typename G_Tp>
constexpr bool
operator>(nullopt_t, const optional<G_Tp>& /* __rhs */) noexcept
{ return false; }
template<typename G_Tp>
constexpr bool
operator<=(const optional<G_Tp>& g_lhs, nullopt_t) noexcept
{ return !g_lhs; }
template<typename G_Tp>
constexpr bool
operator<=(nullopt_t, const optional<G_Tp>& /* __rhs */) noexcept
{ return true; }
template<typename G_Tp>
constexpr bool
operator>=(const optional<G_Tp>& /* __lhs */, nullopt_t) noexcept
{ return true; }
template<typename G_Tp>
constexpr bool
operator>=(nullopt_t, const optional<G_Tp>& g_rhs) noexcept
{ return !g_rhs; }
#endif // three-way-comparison
// Comparisons with value type.
template<typename G_Tp, typename G_Up,
G_Requires<not_<g_is_optional<G_Up>>> = false>
constexpr auto
operator== [[nodiscard]] (const optional<G_Tp>& g_lhs, const G_Up& g_rhs)
-> g_optional_eq_t<G_Tp, G_Up>
{
if (g_lhs.has_value())
return *g_lhs == g_rhs;
return false;
}
template<typename G_Tp, typename G_Up,
G_Requires<not_<g_is_optional<G_Tp>>> = false>
constexpr auto
operator== [[nodiscard]] (const G_Tp& g_lhs, const optional<G_Up>& g_rhs)
-> g_optional_eq_t<G_Tp, G_Up>
{
if (g_rhs.has_value())
return g_lhs == *g_rhs;
return false;
}
template<typename G_Tp, typename G_Up,
G_Requires<not_<g_is_optional<G_Up>>> = false>
constexpr auto
operator!= [[nodiscard]] (const optional<G_Tp>& g_lhs, const G_Up& g_rhs)
-> g_optional_ne_t<G_Tp, G_Up>
{
if (g_lhs.has_value())
return *g_lhs != g_rhs;
return true;
}
template<typename G_Tp, typename G_Up,
G_Requires<not_<g_is_optional<G_Tp>>> = false>
constexpr auto
operator!= [[nodiscard]] (const G_Tp& g_lhs, const optional<G_Up>& g_rhs)
-> g_optional_ne_t<G_Tp, G_Up>
{
if (g_rhs.has_value())
return g_lhs != *g_rhs;
return true;
}
template<typename G_Tp, typename G_Up,
G_Requires<not_<g_is_optional<G_Up>>> = false>
constexpr auto
operator< [[nodiscard]] (const optional<G_Tp>& g_lhs, const G_Up& g_rhs)
-> g_optional_lt_t<G_Tp, G_Up>
{
if (g_lhs.has_value())
return *g_lhs < g_rhs;
return true;
}
template<typename G_Tp, typename G_Up,
G_Requires<not_<g_is_optional<G_Tp>>> = false>
constexpr auto
operator< [[nodiscard]] (const G_Tp& g_lhs, const optional<G_Up>& g_rhs)
-> g_optional_lt_t<G_Tp, G_Up>
{
if (g_rhs.has_value())
return g_lhs < *g_rhs;
return false;
}
template<typename G_Tp, typename G_Up,
G_Requires<not_<g_is_optional<G_Up>>> = false>
constexpr auto
operator> [[nodiscard]] (const optional<G_Tp>& g_lhs, const G_Up& g_rhs)
-> g_optional_gt_t<G_Tp, G_Up>
{
if (g_lhs.has_value())
return *g_lhs > g_rhs;
return false;
}
template<typename G_Tp, typename G_Up,
G_Requires<not_<g_is_optional<G_Tp>>> = false>
constexpr auto
operator> [[nodiscard]] (const G_Tp& g_lhs, const optional<G_Up>& g_rhs)
-> g_optional_gt_t<G_Tp, G_Up>
{
if (g_rhs.has_value())
return g_lhs > *g_rhs;
return true;
}
template<typename G_Tp, typename G_Up,
G_Requires<not_<g_is_optional<G_Up>>> = false>
constexpr auto
operator<= [[nodiscard]] (const optional<G_Tp>& g_lhs, const G_Up& g_rhs)
-> g_optional_le_t<G_Tp, G_Up>
{
if (g_lhs.has_value())
return *g_lhs <= g_rhs;
return true;
}
template<typename G_Tp, typename G_Up,
G_Requires<not_<g_is_optional<G_Tp>>> = false>
constexpr auto
operator<= [[nodiscard]] (const G_Tp& g_lhs, const optional<G_Up>& g_rhs)
-> g_optional_le_t<G_Tp, G_Up>
{
if (g_rhs.has_value())
return g_lhs <= *g_rhs;
return false;
}
template<typename G_Tp, typename G_Up,
G_Requires<not_<g_is_optional<G_Up>>> = false>
constexpr auto
operator>= [[nodiscard]] (const optional<G_Tp>& g_lhs, const G_Up& g_rhs)
-> g_optional_ge_t<G_Tp, G_Up>
{
if (g_lhs.has_value())
return *g_lhs >= g_rhs;
return false;
}
template<typename G_Tp, typename G_Up,
G_Requires<not_<g_is_optional<G_Tp>>> = false>
constexpr auto
operator>= [[nodiscard]] (const G_Tp& g_lhs, const optional<G_Up>& g_rhs)
-> g_optional_ge_t<G_Tp, G_Up>
{
if (g_rhs.has_value())
return g_lhs >= *g_rhs;
return true;
}
#ifdef __cpp_lib_three_way_comparison
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3746. optional's spaceship with U with a type derived from optional
// causes infinite constraint meta-recursion
template<typename G_Tp>
concept g_is_derived_from_optional = requires (const G_Tp& g_t) {
[]<typename G_Up>(const optional<G_Up>&){ }(g_t);
};
template<typename G_Tp, typename G_Up>
requires (!g_is_derived_from_optional<G_Up>)
&& requires { typename std::compare_three_way_result_t<G_Tp, G_Up>; }
&& std::three_way_comparable_with<G_Tp, G_Up>
constexpr std::compare_three_way_result_t<G_Tp, G_Up>
operator<=> [[nodiscard]] (const optional<G_Tp>& g_x, const G_Up& g_v)
{ return bool(g_x) ? *g_x <=> g_v : std::strong_ordering::less; }
#endif
// Swap and creation functions.
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2748. swappable traits for optionals
template<typename G_Tp>
constexpr
inline std::enable_if_t<std::is_move_constructible<G_Tp>::value && g_is_swappable<G_Tp>::value>
swap(optional<G_Tp>& g_lhs, optional<G_Tp>& g_rhs)
noexcept(noexcept(g_lhs.swap(g_rhs)))
{ g_lhs.swap(g_rhs); }
// We deviate from standard, that do not declared separate swap overload
// from optional<T&>.
template<typename G_Tp>
constexpr void
swap(optional<G_Tp&>& g_lhs, optional<G_Tp&>& g_rhs) noexcept
{ g_lhs.swap(g_rhs); }
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2766. Swapping non-swappable types
template<typename G_Tp>
std::enable_if_t<!(std::is_move_constructible<G_Tp>::value && g_is_swappable<G_Tp>::value)>
swap(optional<G_Tp>&, optional<G_Tp>&) = delete;
template<int = 0, typename G_Tp>
constexpr
std::enable_if_t<std::is_constructible<std::decay_t<G_Tp>, G_Tp>::value,
optional<std::decay_t<G_Tp>>>
make_optional(G_Tp&& g_t)
noexcept(std::is_nothrow_constructible<optional<std::decay_t<G_Tp>>, G_Tp>::value)
{ return optional<std::decay_t<G_Tp>>( std::forward<G_Tp>(g_t) ); }
template<typename G_Tp, typename... G_Args>
constexpr
std::enable_if_t<std::is_constructible<G_Tp, G_Args...>::value,
optional<G_Tp>>
make_optional(G_Args&&... g_args)
noexcept(std::is_nothrow_constructible<G_Tp, G_Args...>::value)
{ return optional<G_Tp>( in_place, std::forward<G_Args>(g_args)... ); }
template<typename G_Tp, typename G_Up, typename... G_Args>
constexpr
std::enable_if_t<std::is_constructible<G_Tp, std::initializer_list<G_Up>&, G_Args...>::value,
optional<G_Tp>>
make_optional(std::initializer_list<G_Up> g_il, G_Args&&... g_args)
noexcept(std::is_nothrow_constructible<G_Tp, std::initializer_list<G_Up>&, G_Args...>::value)
{ return optional<G_Tp>( in_place, g_il, std::forward<G_Args>(g_args)... ); }
// Hash.
template<typename G_Tp, typename G_Up = std::remove_const_t<G_Tp>>
struct g_optional_hash
{
#if __cplusplus < 202002L
using result_type = size_t;
using argument_type = optional<G_Tp>;
#endif
size_t
operator()(const optional<G_Tp>& g_t) const
noexcept(noexcept(std::hash<G_Up>{}(*g_t)))
{
// We pick an arbitrary hash for disengaged optionals which hopefully
// usual values of _Tp won't typically hash to.
constexpr size_t g_magic_disengaged_hash = static_cast<size_t>(-3333);
return g_t ? std::hash<G_Up>{}(*g_t) : g_magic_disengaged_hash;
}
};
#if __cpp_deduction_guides >= 201606
template <typename G_Tp> optional(G_Tp) -> optional<G_Tp>;
#endif
} // namespace gcc
namespace std {
template<typename G_Tp>
struct hash<gcc::optional<G_Tp>>
// hash for optional<T&> is disabled because is_hash_enabled_for<T&> is false
: public std::conditional_t<
gcc::g_is_hash_enabled_for<std::remove_const_t<G_Tp>>::value,
gcc::g_optional_hash<G_Tp>,
gcc::g_hash_not_enabled<G_Tp>>
{ };
} // namespace std
#endif // GCC_BACKPORT_GLIBCXX_OPTIONAL