[PATCH 1/2] libstdc++: Implement optional<T&> from P2988R12 [PR121748]
Tomasz Kaminski
tkaminsk@redhat.com
Tue Oct 21 08:31:18 GMT 2025
On Tue, Oct 21, 2025 at 10:29 AM Jonathan Wakely <jwakely@redhat.com> wrote:
> On Tue, 21 Oct 2025 at 08:58, Tomasz Kaminski <tkaminsk@redhat.com> wrote:
> >
> >
> >
> > On Tue, Oct 21, 2025 at 9:27 AM Jonathan Wakely <jwakely.gcc@gmail.com>
> wrote:
> >>
> >>
> >>
> >> On Fri, 17 Oct 2025, 16:17 Tomasz Kamiński, <tkaminsk@redhat.com>
> wrote:
> >>>
> >>> From: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
> >>>
> >>> This patch implements optional<T&> based on the P2988R12 paper,
> incorporating
> >>> corrections for LWG4300, LWG4304, and LWG3467. The resolution for
> LWG4015
> >>> is also extended to cover optional<T&>.
> >>>
> >>> In optional<T> use std::move(__t).operator*()
> >>
> >>
> >>
> >> Doesn't this mean repeating the !empty assertion even though we already
> checked it?
> >
> > Yes, it does. I assume the optimizer will be able to remove it. I can
> add another _M_fwd
> > function to both, to avoid that.
>
> Yes, I would prefer that. The optimizer should be able to remove the
> redundant assertions, but that won't happen at -O0, and adding more
> work for the optimizer makes it less likely that things will be fully
> inlined.
>
I will do that, but for PR for LWG4015 should still use operator*() in
these places,
as the check can be omitted due as-if.
>
> >>
> >>
> >>
> >>> to correctly propagate the value
> >>> during move construction from optional<T&>. This is necessary because
> moving
> >>> an optional<T&> must not move the contained object, which is the key
> distinction
> >>> between *std::move(opt) and std::move(*opt).
> >>>
> >>> The implementation deviates from the standard by providing a separate
> std::swap
> >>> overload for std::optional<T&>, which simplifies preserving the
> resolution of
> >>> LWG2766.
> >>>
> >>> This introduces a few changes to make_optional behavior (see included
> test):
> >>> * some previously valid uses of make_optional<T>({...}) (where T is
> not a
> >>> reference type) now become ill-formed (see
> optional/make_optional_neg.cc).
> >>> * make_optional<T&>(t) and make_optional<const T&>(ct), where
> decltype(t) is T&,
> >>> and decltype(cy) is const T& now produce optional<T&> and
> optional<const T&>
> >>> respectively, instead of optional<T>.
> >>> * a few other uses of make_optional<R> with reference types are now
> ill-formed.
> >>>
> >>> PR libstdc++/121748
> >>>
> >>> libstdc++-v3/ChangeLog:
> >>>
> >>> * include/bits/version.def: Bump value for optional,
> >>> * include/bits/version.h: Regenerate.
> >>> * include/std/optional
> (std::__is_valid_contained_type_for_optional):
> >>> Define.
> >>> (std::optional<T>): Use __is_valid_contained_type_for_optional.
> >>> (optional<T>(const optional<_Up>&),
> optional<T>(optional<_Up>&&))
> >>> (optional<T>::operator=(const optional<_Up>&))
> >>> (optional<T>::operator=(optional<_Up>&&)): Replace
> std::move(x._M_get())
> >>> with std::move(x).operator*(), and x._M_get() with
> x.operator*().
> >>> (optional<T>::and_then): Remove uncessary remove_cvref_t.
> >>> (std::optional<T&>): Define new partial specialization.
> >>> (std::swap(std::optional<T&>, std::optional<T&>)): Define.
> >>> (std::make_optional(_Tp&&)): Add non-type template parameter.
> >>> (std::make_optional): Use parenthesis to constructor optional.
> >>> (std::hash<optional<T>>): Add comment.
> >>> * testsuite/20_util/optional/make_optional-2.cc: Guarded not
> longer
> >>> working example.
> >>> * testsuite/20_util/optional/relops/constrained.cc: Expand
> test to
> >>> cover optionals of reference.
> >>> * testsuite/20_util/optional/requirements.cc: Ammend for
> optional<T&>.
> >>> * testsuite/20_util/optional/requirements_neg.cc: Likewise.
> >>> * testsuite/20_util/optional/version.cc: Test new value of
> >>> __cpp_lib_optional.
> >>> * testsuite/20_util/optional/make_optional_neg.cc: New test.
> >>> * testsuite/20_util/optional/monadic/ref_neg.cc: New test.
> >>> * testsuite/20_util/optional/ref/access.cc: New test.
> >>> * testsuite/20_util/optional/ref/assign.cc: New test.
> >>> * testsuite/20_util/optional/ref/cons.cc: New test.
> >>> * testsuite/20_util/optional/ref/internal_traits.cc: New test.
> >>> * testsuite/20_util/optional/ref/make_optional/1.cc: New test.
> >>> *
> testsuite/20_util/optional/ref/make_optional/from_args_neg.cc: New test.
> >>> *
> testsuite/20_util/optional/ref/make_optional/from_lvalue_neg.cc: New test.
> >>> *
> testsuite/20_util/optional/ref/make_optional/from_rvalue_neg.cc: New test.
> >>> * testsuite/20_util/optional/ref/monadic.cc: New test.
> >>> * testsuite/20_util/optional/ref/relops.cc: New test.
> >>>
> >>> Co-authored-by: Tomasz Kamiński <tkaminsk@redhat.com>
> >>> ---
> >>> Tested on x86_64-linux locally. *optional* test also tested with all
> standard modes.
> >>> OK for trunk?
> >>>
> >>> libstdc++-v3/include/bits/version.def | 8 +-
> >>> libstdc++-v3/include/bits/version.h | 7 +-
> >>> libstdc++-v3/include/std/optional | 360 ++++++++++++++-
> >>> .../20_util/optional/make_optional-2.cc | 2 +
> >>> .../20_util/optional/make_optional_neg.cc | 20 +
> >>> .../20_util/optional/monadic/ref_neg.cc | 20 +
> >>> .../testsuite/20_util/optional/ref/access.cc | 119 +++++
> >>> .../testsuite/20_util/optional/ref/assign.cc | 430 ++++++++++++++++++
> >>> .../testsuite/20_util/optional/ref/cons.cc | 356 +++++++++++++++
> >>> .../20_util/optional/ref/internal_traits.cc | 11 +
> >>> .../20_util/optional/ref/make_optional/1.cc | 74 +++
> >>> .../ref/make_optional/from_args_neg.cc | 43 ++
> >>> .../ref/make_optional/from_lvalue_neg.cc | 40 ++
> >>> .../ref/make_optional/from_rvalue_neg.cc | 38 ++
> >>> .../testsuite/20_util/optional/ref/monadic.cc | 192 ++++++++
> >>> .../testsuite/20_util/optional/ref/relops.cc | 229 ++++++++++
> >>> .../20_util/optional/relops/constrained.cc | 86 +++-
> >>> .../20_util/optional/requirements.cc | 6 +-
> >>> .../20_util/optional/requirements_neg.cc | 17 +-
> >>> .../testsuite/20_util/optional/version.cc | 4 +-
> >>> 20 files changed, 2029 insertions(+), 33 deletions(-)
> >>> create mode 100644
> libstdc++-v3/testsuite/20_util/optional/make_optional_neg.cc
> >>> create mode 100644
> libstdc++-v3/testsuite/20_util/optional/monadic/ref_neg.cc
> >>> create mode 100644
> libstdc++-v3/testsuite/20_util/optional/ref/access.cc
> >>> create mode 100644
> libstdc++-v3/testsuite/20_util/optional/ref/assign.cc
> >>> create mode 100644 libstdc++-v3/testsuite/20_util/optional/ref/cons.cc
> >>> create mode 100644
> libstdc++-v3/testsuite/20_util/optional/ref/internal_traits.cc
> >>> create mode 100644
> libstdc++-v3/testsuite/20_util/optional/ref/make_optional/1.cc
> >>> create mode 100644
> libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_args_neg.cc
> >>> create mode 100644
> libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_lvalue_neg.cc
> >>> create mode 100644
> libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_rvalue_neg.cc
> >>> create mode 100644
> libstdc++-v3/testsuite/20_util/optional/ref/monadic.cc
> >>> create mode 100644
> libstdc++-v3/testsuite/20_util/optional/ref/relops.cc
> >>>
> >>> diff --git a/libstdc++-v3/include/bits/version.def
> b/libstdc++-v3/include/bits/version.def
> >>> index 1118da4b541..be15442a50f 100644
> >>> --- a/libstdc++-v3/include/bits/version.def
> >>> +++ b/libstdc++-v3/include/bits/version.def
> >>> @@ -878,12 +878,16 @@ ftms = {
> >>> // Moved down here (after concepts) by topological sort.
> >>> ftms = {
> >>> name = optional;
> >>> - values = {
> >>> + values = { // optional<T&>
> >>> + v = 202506;
> >>> + cxxmin = 26;
> >>
> >>
> >> This should repeat the extra_cond on the previous value, otherwise we'd
> define it to > 202110 even though the monadic ops aren't supported.
> >>
> >>
> >>> + };
> >>> + values = { // monadic operations
> >>> v = 202110;
> >>> cxxmin = 23;
> >>> extra_cond = "__glibcxx_concepts";
> >>> };
> >>> - values = {
> >>> + values = { // full constexpr support
> >>> v = 202106;
> >>> cxxmin = 20;
> >>> };
> >>> diff --git a/libstdc++-v3/include/bits/version.h
> b/libstdc++-v3/include/bits/version.h
> >>> index c452bbeec8e..e98ecdf94e7 100644
> >>> --- a/libstdc++-v3/include/bits/version.h
> >>> +++ b/libstdc++-v3/include/bits/version.h
> >>> @@ -985,7 +985,12 @@
> >>> #undef __glibcxx_want_concepts
> >>>
> >>> #if !defined(__cpp_lib_optional)
> >>> -# if (__cplusplus >= 202100L) && (__glibcxx_concepts)
> >>> +# if (__cplusplus > 202302L)
> >>> +# define __glibcxx_optional 202506L
> >>> +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_optional)
> >>> +# define __cpp_lib_optional 202506L
> >>> +# endif
> >>> +# elif (__cplusplus >= 202100L) && (__glibcxx_concepts)
> >>> # define __glibcxx_optional 202110L
> >>> # if defined(__glibcxx_want_all) || defined(__glibcxx_want_optional)
> >>> # define __cpp_lib_optional 202110L
> >>> diff --git a/libstdc++-v3/include/std/optional
> b/libstdc++-v3/include/std/optional
> >>> index e5051d72c82..e6f3a985f50 100644
> >>> --- a/libstdc++-v3/include/std/optional
> >>> +++ b/libstdc++-v3/include/std/optional
> >>> @@ -777,6 +777,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> # define _GLIBCXX_USE_CONSTRAINTS_FOR_OPTIONAL 1
> >>> #endif
> >>>
> >>> + template<typename _Tp>
> >>> + inline constexpr bool __is_valid_contained_type_for_optional =
> >>> + (
> >>> +#if __cpp_lib_optional >= 202506L
> >>> + is_lvalue_reference_v<_Tp> ||
> >>> +#endif
> >>> + (is_object_v<_Tp> && is_destructible_v<_Tp> &&
> !is_array_v<_Tp>)
> >>> + )
> >>> + && !is_same_v<remove_cv_t<remove_reference_t<_Tp>>, nullopt_t>
> >>> + && !is_same_v<remove_cv_t<remove_reference_t<_Tp>>, in_place_t>;
> >>> +
> >>> /**
> >>> * @brief Class template for optional values.
> >>> */
> >>> @@ -795,9 +806,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> // Unique tag type.
> >>> optional<_Tp>>
> >>> {
> >>> - static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>);
> >>> - static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>);
> >>> - static_assert(is_object_v<_Tp> && !is_array_v<_Tp>);
> >>> + static_assert(__is_valid_contained_type_for_optional<_Tp>);
> >>>
> >>> private:
> >>> using _Base = _Optional_base<_Tp>;
> >>> @@ -894,7 +903,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> noexcept(is_nothrow_constructible_v<_Tp, const _Up&>)
> >>> {
> >>> if (__t)
> >>> - emplace(__t._M_get());
> >>> + emplace(__t.operator*());
> >>> }
> >>>
> >>> template<typename _Up>
> >>> @@ -906,7 +915,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> noexcept(is_nothrow_constructible_v<_Tp, _Up>)
> >>> {
> >>> if (__t)
> >>> - emplace(std::move(__t._M_get()));
> >>> + emplace(std::move(__t).operator*());
> >>> }
> >>>
> >>> template<typename... _Args>
> >>> @@ -956,7 +965,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> noexcept(is_nothrow_constructible_v<_Tp, const _Up&>)
> >>> {
> >>> if (__t)
> >>> - emplace(__t._M_get());
> >>> + emplace(__t.operator*());
> >>> }
> >>>
> >>> template<typename _Up,
> >>> @@ -969,7 +978,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> noexcept(is_nothrow_constructible_v<_Tp, const _Up&>)
> >>> {
> >>> if (__t)
> >>> - emplace(__t._M_get());
> >>> + emplace(__t.operator*());
> >>> }
> >>>
> >>> template<typename _Up,
> >>> @@ -982,7 +991,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> noexcept(is_nothrow_constructible_v<_Tp, _Up>)
> >>> {
> >>> if (__t)
> >>> - emplace(std::move(__t._M_get()));
> >>> + emplace(std::move(__t).operator*());
> >>> }
> >>>
> >>> template<typename _Up,
> >>> @@ -995,7 +1004,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> noexcept(is_nothrow_constructible_v<_Tp, _Up>)
> >>> {
> >>> if (__t)
> >>> - emplace(std::move(__t._M_get()));
> >>> + emplace(std::move(__t).operator*());
> >>> }
> >>>
> >>> template<typename... _Args,
> >>> @@ -1074,9 +1083,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> if (__u)
> >>> {
> >>> if (this->_M_is_engaged())
> >>> - this->_M_get() = __u._M_get();
> >>> + this->_M_get() = __u.operator*();
> >>> else
> >>> - this->_M_construct(__u._M_get());
> >>> + this->_M_construct(__u.operator*());
> >>> }
> >>> else
> >>> {
> >>> @@ -1108,9 +1117,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> if (__u)
> >>> {
> >>> if (this->_M_is_engaged())
> >>> - this->_M_get() = std::move(__u._M_get());
> >>> + this->_M_get() = std::move(__u).operator*();
> >>> else
> >>> - this->_M_construct(std::move(__u._M_get()));
> >>> + this->_M_construct(std::move(__u).operator*());
> >>> }
> >>> else
> >>> {
> >>> @@ -1310,7 +1319,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> and_then(_Fn&& __f) &
> >>> {
> >>> using _Up = remove_cvref_t<invoke_result_t<_Fn, _Tp&>>;
> >>> - static_assert(__is_optional_v<remove_cvref_t<_Up>>,
> >>> + static_assert(__is_optional_v<_Up>,
> >>> "the function passed to
> std::optional<T>::and_then "
> >>> "must return a std::optional");
> >>> if (has_value())
> >>> @@ -1338,7 +1347,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> and_then(_Fn&& __f) &&
> >>> {
> >>> using _Up = remove_cvref_t<invoke_result_t<_Fn, _Tp>>;
> >>> - static_assert(__is_optional_v<remove_cvref_t<_Up>>,
> >>> + static_assert(__is_optional_v<_Up>,
> >>> "the function passed to
> std::optional<T>::and_then "
> >>> "must return a std::optional");
> >>> if (has_value())
> >>> @@ -1352,7 +1361,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> and_then(_Fn&& __f) const &&
> >>> {
> >>> using _Up = remove_cvref_t<invoke_result_t<_Fn, const _Tp>>;
> >>> - static_assert(__is_optional_v<remove_cvref_t<_Up>>,
> >>> + static_assert(__is_optional_v<_Up>,
> >>> "the function passed to
> std::optional<T>::and_then "
> >>> "must return a std::optional");
> >>> if (has_value())
> >>> @@ -1453,6 +1462,305 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> #endif
> >>> };
> >>>
> >>> +#if __cpp_lib_optional >= 202506L // C++26
> >>> + template<typename _Tp>
> >>> + class optional<_Tp&>
> >>> + {
> >>> + static_assert(__is_valid_contained_type_for_optional<_Tp&>);
> >>> +
> >>> + public:
> >>> + using value_type = _Tp;
> >>> + using iterator = __gnu_cxx::__normal_iterator<_Tp*, optional>;
> >>> +
> >>> + // Constructors.
> >>> + constexpr optional() noexcept = default;
> >>> + constexpr optional(nullopt_t) noexcept : optional() {}
> >>> + constexpr optional(const optional&) noexcept = default;
> >>> +
> >>> + template<typename _Arg>
> >>> + requires is_constructible_v<_Tp&, _Arg>
> >>> + && (!reference_constructs_from_temporary_v<_Tp&, _Arg>)
> >>> + explicit constexpr
> >>> + optional(in_place_t, _Arg&& __arg)
> >>> + {
> >>> + __convert_ref_init_val(std::forward<_Arg>(__arg));
> >>> + }
> >>> +
> >>> + template<typename _Up>
> >>> + requires (!is_same_v<remove_cvref_t<_Up>, optional>)
> >>> + && (!is_same_v<remove_cvref_t<_Up>, in_place_t>)
> >>> + && is_constructible_v<_Tp&, _Up>
> >>> + && (!reference_constructs_from_temporary_v<_Tp&, _Up>)
> >>> + explicit(!is_convertible_v<_Up, _Tp&>)
> >>> + constexpr
> >>> + optional(_Up&& __u)
> >>> + noexcept(is_nothrow_constructible_v<_Tp&, _Up>)
> >>> + {
> >>> + __convert_ref_init_val(std::forward<_Up>(__u));
> >>> + }
> >>> +
> >>> + template<typename _Up>
> >>> + requires (!is_same_v<remove_cvref_t<_Up>, optional>)
> >>> + && (!is_same_v<remove_cvref_t<_Up>, in_place_t>)
> >>> + && is_constructible_v<_Tp&, _Up>
> >>> + && reference_constructs_from_temporary_v<_Tp&, _Up>
> >>> + explicit(!is_convertible_v<_Up, _Tp&>)
> >>> + constexpr
> >>> + optional(_Up&& __u) = delete;
> >>> +
> >>> + // optional<U> &
> >>> + template<typename _Up>
> >>> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>)
> >>> + && (!is_same_v<_Tp&, _Up>)
> >>> + && is_constructible_v<_Tp&, _Up&>
> >>> + && (!reference_constructs_from_temporary_v<_Tp&, _Up&>)
> >>> + explicit(!is_convertible_v<_Up&, _Tp&>)
> >>> + constexpr
> >>> + optional(optional<_Up>& __rhs)
> >>> + noexcept(is_nothrow_constructible_v<_Tp&, _Up&>)
> >>> + {
> >>> + if (__rhs)
> >>> + __convert_ref_init_val(__rhs.operator*());
> >>> + }
> >>> +
> >>> + template<typename _Up>
> >>> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>)
> >>> + && (!is_same_v<_Tp&, _Up>)
> >>> + && is_constructible_v<_Tp&, _Up&>
> >>> + && reference_constructs_from_temporary_v<_Tp&, _Up&>
> >>> + explicit(!is_convertible_v<_Up&, _Tp&>)
> >>> + constexpr
> >>> + optional(optional<_Up>& __rhs) = delete;
> >>> +
> >>> + // const optional<U>&
> >>> + template<typename _Up>
> >>> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>)
> >>> + && (!is_same_v<_Tp&, _Up>)
> >>> + && is_constructible_v<_Tp&, const _Up&>
> >>> + && (!reference_constructs_from_temporary_v<_Tp&, const _Up&>)
> >>> + explicit(!is_convertible_v<const _Up&, _Tp&>)
> >>> + constexpr
> >>> + optional(const optional<_Up>& __rhs)
> >>> + noexcept(is_nothrow_constructible_v<_Tp&, _Up&>)
> >>> + {
> >>> + if (__rhs)
> >>> + __convert_ref_init_val(__rhs.operator*());
> >>> + }
> >>> +
> >>> + template<typename _Up>
> >>> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>)
> >>> + && (!is_same_v<_Tp&, _Up>)
> >>> + && is_constructible_v<_Tp&, const _Up&>
> >>> + && reference_constructs_from_temporary_v<_Tp&, const _Up&>
> >>> + explicit(!is_convertible_v<const _Up&, _Tp&>)
> >>> + constexpr
> >>> + optional(const optional<_Up>& __rhs) = delete;
> >>> +
> >>> + // optional<U>&&
> >>> + template<typename _Up>
> >>> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>)
> >>> + && (!is_same_v<_Tp&, _Up>)
> >>> + && is_constructible_v<_Tp&, _Up>
> >>> + && (!reference_constructs_from_temporary_v<_Tp&, _Up>)
> >>> + explicit(!is_convertible_v<_Up, _Tp&>)
> >>> + constexpr
> >>> + optional(optional<_Up>&& __rhs)
> >>> + noexcept(is_nothrow_constructible_v<_Tp&, _Up>)
> >>> + {
> >>> + if (__rhs)
> >>> + __convert_ref_init_val(std::move(__rhs).operator*());
> >>> + }
> >>> +
> >>> + template<typename _Up>
> >>> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>)
> >>> + && (!is_same_v<_Tp&, _Up>)
> >>> + && is_constructible_v<_Tp&, _Up>
> >>> + && reference_constructs_from_temporary_v<_Tp&, _Up>
> >>> + explicit(!is_convertible_v<_Up, _Tp&>)
> >>> + constexpr
> >>> + optional(optional<_Up>&& __rhs) = delete;
> >>> +
> >>> + // const optional<U>&&
> >>> + template<typename _Up>
> >>> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>)
> >>> + && (!is_same_v<_Tp&, _Up>)
> >>> + && is_constructible_v<_Tp&, const _Up>
> >>> + && (!reference_constructs_from_temporary_v<_Tp&, _Up>)
> >>> + explicit(!is_convertible_v<const _Up, _Tp&>)
> >>> + constexpr
> >>> + optional(const optional<_Up>&& __rhs)
> >>> + noexcept(is_nothrow_constructible_v<_Tp&, const _Up>)
> >>> + {
> >>> + if (__rhs)
> >>> + __convert_ref_init_val(std::move(__rhs).operator*());
> >>> + }
> >>> +
> >>> + template<typename _Up>
> >>> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>)
> >>> + && (!is_same_v<_Tp&, _Up>)
> >>> + && is_constructible_v<_Tp&, const _Up>
> >>> + && reference_constructs_from_temporary_v<_Tp&, const _Up>
> >>> + explicit(!is_convertible_v<const _Up, _Tp&>)
> >>> + constexpr
> >>> + optional(const optional<_Up>&& __rhs) = delete;
> >>> +
> >>> + constexpr ~optional() = default;
> >>> +
> >>> + // Assignment.
> >>> + constexpr optional& operator=(nullopt_t) noexcept
> >>> + {
> >>> + _M_val = nullptr;
> >>> + return *this;
> >>> + }
> >>> +
> >>> + constexpr optional& operator=(const optional&) noexcept =
> default;
> >>> +
> >>> + template<typename _Up>
> >>> + requires is_constructible_v<_Tp&, _Up>
> >>> + && (!reference_constructs_from_temporary_v<_Tp&, _Up>)
> >>> + constexpr _Tp&
> >>> + emplace(_Up&& __u)
> >>> + noexcept(is_nothrow_constructible_v<_Tp&, _Up>)
> >>> + {
> >>> + __convert_ref_init_val(std::forward<_Up>(__u));
> >>> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> >>> + // 4300. Missing Returns: element in optional<T&>::emplace
> >>> + return *_M_val;
> >>> + }
> >>> +
> >>> + // Swap.
> >>> + constexpr void swap(optional& __rhs) noexcept
> >>> + { std::swap(_M_val, __rhs._M_val); }
> >>> +
> >>> + // Iterator support.
> >>> + constexpr iterator begin() const noexcept
> >>> + {
> >>> + return iterator(_M_val);
> >>> + }
> >>> +
> >>> + constexpr iterator end() const noexcept
> >>> + {
> >>> + return begin() + has_value();
> >>> + }
> >>> +
> >>> + // Observers.
> >>> + constexpr _Tp* operator->() const noexcept
> >>> + {
> >>> + __glibcxx_assert(_M_val); // hardened precondition
> >>> + return _M_val;
> >>> + }
> >>> +
> >>> + constexpr _Tp& operator*() const noexcept
> >>> + {
> >>> + __glibcxx_assert(_M_val); // hardened precondition
> >>> + return *_M_val;
> >>> + }
> >>> +
> >>> + constexpr explicit operator bool() const noexcept
> >>> + {
> >>> + return _M_val;
> >>> + }
> >>> +
> >>> + constexpr bool has_value() const noexcept
> >>> + {
> >>> + return _M_val;
> >>> + }
> >>> +
> >>> + constexpr _Tp& value() const
> >>> + {
> >>> + if (_M_val)
> >>> + return *_M_val;
> >>> + __throw_bad_optional_access();
> >>> + }
> >>> +
> >>> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> >>> + // 4304. std::optional<NonReturnable&> is ill-formed due to
> value_or
> >>> + template<typename _Up = remove_cv_t<_Tp>>
> >>> + requires is_object_v<_Tp> && (!is_array_v<_Tp>)
> >>> + constexpr decay_t<_Tp>
> >>> + value_or(_Up&& __u) const
> >>> + {
> >>> + using _Xp = remove_cv_t<_Tp>;
> >>> + static_assert(is_constructible_v<_Xp, _Tp&>);
> >>> + static_assert(is_convertible_v<_Up, _Xp>);
> >>> + return _M_val ? *_M_val :
> static_cast<_Xp>(std::forward<_Up>(__u));
> >>> + }
> >>> +
> >>> + // Monadic operations.
> >>> + template<typename _Fn>
> >>> + constexpr auto
> >>> + and_then(_Fn&& __f) const
> >>> + {
> >>> + using _Up = remove_cvref_t<invoke_result_t<_Fn, _Tp&>>;
> >>> + static_assert(__is_optional_v<_Up>,
> >>> + "the function passed to
> std::optional<T&>::and_then "
> >>> + "must return a std::optional");
> >>> + if (has_value())
> >>> + return std::__invoke(std::forward<_Fn>(__f), *_M_val);
> >>> + else
> >>> + return _Up();
> >>> + }
> >>> +
> >>> + template<typename _Fn>
> >>> + constexpr
> >>> + optional<remove_cv_t<invoke_result_t<_Fn, _Tp&>>>
> >>> + transform(_Fn&& __f) const
> >>> + {
> >>> + using _Up = remove_cv_t<invoke_result_t<_Fn, _Tp&>>;
> >>> + if (has_value())
> >>> + return optional<_Up>(_Optional_func<_Fn>{__f}, *_M_val);
> >>> + else
> >>> + return optional<_Up>();
> >>> + }
> >>> +
> >>> + template<typename _Fn>
> >>> + requires invocable<_Fn>
> >>> + constexpr
> >>> + optional
> >>> + or_else(_Fn&& __f) const
> >>> + {
> >>> +
> static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Fn>>, optional>,
> >>> + "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<_Fn>(__f)();
> >>> + }
> >>> +
> >>> + // Modifiers.
> >>> + constexpr void reset() noexcept
> >>> + {
> >>> + _M_val = nullptr;
> >>> + }
> >>> +
> >>> + private:
> >>> + _Tp *_M_val = nullptr;
> >>> +
> >>> + template<typename _Up> friend class optional;
> >>> +
> >>> + template<typename _Up>
> >>> + constexpr
> >>> + void
> >>> + __convert_ref_init_val(_Up&& __u)
> >>> + noexcept
> >>> + {
> >>> + _Tp& __r(std::forward<_Up>(__u));
> >>> + _M_val = std::addressof(__r);
> >>> + }
> >>> +
> >>> + template<typename _Fn, typename _Value>
> >>> + explicit constexpr
> >>> + optional(_Optional_func<_Fn> __f, _Value&& __v)
> >>> + {
> >>> + _Tp& __r = std::__invoke(std::forward<_Fn>(__f._M_f),
> std::forward<_Value>(__v));
> >>> + _M_val = std::addressof(__r);
> >>> + }
> >>> + };
> >>> +#endif // __cpp_lib_optional >= 202506L
> >>> +
> >>> template<typename _Tp>
> >>> using __optional_relop_t =
> >>> enable_if_t<is_convertible_v<_Tp, bool>, bool>;
> >>> @@ -1740,19 +2048,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> noexcept(noexcept(__lhs.swap(__rhs)))
> >>> { __lhs.swap(__rhs); }
> >>>
> >>> +#if __cpp_lib_optional >= 202506L
> >>> + // We deviate from standard, that do not declared separate swap
> overload
> >>> + // from optional<T&>.
> >>> + template<typename _Tp>
> >>> + constexpr void
> >>> + swap(optional<_Tp&>& __lhs, optional<_Tp&>& __rhs) noexcept
> >>> + { __lhs.swap(__rhs); }
> >>> +#endif
> >>> +
> >>> // _GLIBCXX_RESOLVE_LIB_DEFECTS
> >>> // 2766. Swapping non-swappable types
> >>> template<typename _Tp>
> >>> enable_if_t<!(is_move_constructible_v<_Tp> &&
> is_swappable_v<_Tp>)>
> >>> swap(optional<_Tp>&, optional<_Tp>&) = delete;
> >>>
> >>> +#if __cpp_lib_optional >= 202506L
> >>> + template<int = 0, typename _Tp>
> >>> +#else
> >>> template<typename _Tp>
> >>> +#endif
> >>> constexpr
> >>> enable_if_t<is_constructible_v<decay_t<_Tp>, _Tp>,
> >>> optional<decay_t<_Tp>>>
> >>> make_optional(_Tp&& __t)
> >>> noexcept(is_nothrow_constructible_v<optional<decay_t<_Tp>>, _Tp>)
> >>> - { return optional<decay_t<_Tp>>{ std::forward<_Tp>(__t) }; }
> >>> + { return optional<decay_t<_Tp>>( std::forward<_Tp>(__t) ); }
> >>>
> >>> template<typename _Tp, typename... _Args>
> >>> constexpr
> >>> @@ -1760,7 +2081,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> optional<_Tp>>
> >>> make_optional(_Args&&... __args)
> >>> noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
> >>> - { return optional<_Tp>{ in_place, std::forward<_Args>(__args)...
> }; }
> >>> + { return optional<_Tp>( in_place, std::forward<_Args>(__args)...
> ); }
> >>>
> >>> template<typename _Tp, typename _Up, typename... _Args>
> >>> constexpr
> >>> @@ -1768,7 +2089,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>> optional<_Tp>>
> >>> make_optional(initializer_list<_Up> __il, _Args&&... __args)
> >>> noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&,
> _Args...>)
> >>> - { return optional<_Tp>{ in_place, __il,
> std::forward<_Args>(__args)... }; }
> >>> + { return optional<_Tp>( in_place, __il,
> std::forward<_Args>(__args)... ); }
> >>>
> >>> // Hash.
> >>>
> >>> @@ -1796,6 +2117,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>
> >>> template<typename _Tp>
> >>> struct hash<optional<_Tp>>
> >>> + // hash for optional<T&> is disabled because
> is_hash_enabled_for<T&> is false
> >>> : public
> __conditional_t<__is_hash_enabled_for<remove_const_t<_Tp>>,
> >>> __optional_hash<_Tp>,
> >>> __hash_not_enabled<_Tp>>
> >>> diff --git
> a/libstdc++-v3/testsuite/20_util/optional/make_optional-2.cc
> b/libstdc++-v3/testsuite/20_util/optional/make_optional-2.cc
> >>> index f383b519c89..0aa2ac5ed93 100644
> >>> --- a/libstdc++-v3/testsuite/20_util/optional/make_optional-2.cc
> >>> +++ b/libstdc++-v3/testsuite/20_util/optional/make_optional-2.cc
> >>> @@ -70,7 +70,9 @@ static_assert( can_make_optional2<int, int&>::value
> );
> >>> static_assert( noexcept(std::make_optional(i)) );
> >>> static_assert( ! can_make_optional2<void, void>::value );
> >>> static_assert( can_make_optional2<Cont, Cont>::value );
> >>> +#if __cplusplus <= 202302
> >>> static_assert( noexcept(std::make_optional<Cont>({})) );
> >>> +#endif
> >>> static_assert( can_make_optional2<Cont, const Cont&>::value );
> >>> static_assert( ! noexcept(std::make_optional(c)) );
> >>> static_assert( can_make_optional2<Cont, int>::value );
> >>> diff --git
> a/libstdc++-v3/testsuite/20_util/optional/make_optional_neg.cc
> b/libstdc++-v3/testsuite/20_util/optional/make_optional_neg.cc
> >>> new file mode 100644
> >>> index 00000000000..09499477233
> >>> --- /dev/null
> >>> +++ b/libstdc++-v3/testsuite/20_util/optional/make_optional_neg.cc
> >>> @@ -0,0 +1,20 @@
> >>> +// { dg-do compile { target c++17 } }
> >>> +
> >>> +#include <initializer_list>
> >>> +#include <optional>
> >>> +
> >>> +struct S { int x; int* p; };
> >>> +int v;
> >>> +
> >>> +auto os1 = std::make_optional<S>({1, &v}); // { dg-error "no matching
> function for" "" { target c++26 } }
> >>> +
> >>> +struct Cont
> >>> +{
> >>> + Cont();
> >>> + Cont(std::initializer_list<int>, int);
> >>> +};
> >>> +
> >>> +auto oc1 = std::make_optional<Cont>({}); // { dg-error "no matching
> function for" "" { target c++26 } }
> >>> +
> >>> +// { dg-prune-output "no type named 'type' in 'struct std::enable_if"
> }
> >>> +// { dg-prune-output "type/value mismatch at argument 1 in template
> parameter list" }
> >>> diff --git
> a/libstdc++-v3/testsuite/20_util/optional/monadic/ref_neg.cc
> b/libstdc++-v3/testsuite/20_util/optional/monadic/ref_neg.cc
> >>> new file mode 100644
> >>> index 00000000000..ed1b42b1992
> >>> --- /dev/null
> >>> +++ b/libstdc++-v3/testsuite/20_util/optional/monadic/ref_neg.cc
> >>> @@ -0,0 +1,20 @@
> >>> +// { dg-do compile { target c++23 } }
> >>> +
> >>> +#include <optional>
> >>> +
> >>> +struct S { int x; int y; };
> >>> +
> >>> +void test()
> >>> +{
> >>> + std::optional<S> o;
> >>> + const std::optional<S>& co = o;
> >>> +
> >>> + o.transform(&S::x); // { dg-error "from here" "optional<int&>" {
> target c++23_down } }
> >>> + co.transform(&S::x); // { dg-error "from here" "optional<const
> int&>" { target c++23_down } }
> >>> + std::move(o).transform(&S::x); // { dg-error "from here"
> "optional<int&&>" }
> >>> + std::move(co).transform(&S::x); // { dg-error "from here"
> "optional<const int&&>" }
> >>> +}
> >>> +
> >>> +// { dg-prune-output "in a union may not have reference type" }
> >>> +// { dg-prune-output "static assertion failed" }
> >>> +// { dg-prune-output "forming pointer to reference type" }
> >>> diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/access.cc
> b/libstdc++-v3/testsuite/20_util/optional/ref/access.cc
> >>> new file mode 100644
> >>> index 00000000000..37c8ff355a6
> >>> --- /dev/null
> >>> +++ b/libstdc++-v3/testsuite/20_util/optional/ref/access.cc
> >>> @@ -0,0 +1,119 @@
> >>> +// { dg-do run { target c++26 } }
> >>> +
> >>> +#include <optional>
> >>> +#include <type_traits>
> >>> +#include <testsuite_hooks.h>
> >>> +
> >>> +struct NonMovable
> >>> +{
> >>> + constexpr NonMovable() {}
> >>> + NonMovable(NonMovable&&) = delete;
> >>> +};
> >>> +
> >>> +template<typename T>
> >>> +constexpr
> >>> +void test_value(T& t)
> >>> +{
> >>> + std::optional<T&> o1(t);
> >>> + const std::optional<T&> co1(t);
> >>> +
> >>> + static_assert( std::is_same_v<decltype(o1.value()), T&> );
> >>> + VERIFY( &o1.value() == &t );
> >>> +
> >>> + static_assert( std::is_same_v<decltype(co1.value()), T&> );
> >>> + VERIFY( &co1.value() == &t );
> >>> +
> >>> + static_assert( std::is_same_v<decltype(std::move(o1).value()), T&>
> );
> >>> + VERIFY( &std::move(o1).value() == &t );
> >>> +
> >>> + static_assert( std::is_same_v<decltype(std::move(co1).value()), T&>
> );
> >>> + VERIFY( &std::move(co1).value() == &t );
> >>> +
> >>> + std::optional<const T&> o2(t);
> >>> + static_assert( std::is_same_v<decltype(o2.value()), const T&> );
> >>> + VERIFY( &o2.value() == &t );
> >>> +}
> >>> +
> >>> +struct Tracker
> >>> +{
> >>> + int copy = 0;
> >>> + int move = 0;
> >>> +
> >>> + constexpr Tracker(int v) : copy(v), move(v) {}
> >>> +
> >>> + constexpr Tracker(Tracker const& o)
> >>> + : copy(o.copy+1), move(o.move)
> >>> + {}
> >>> +
> >>> + constexpr Tracker(Tracker&& o)
> >>> + : copy(o.copy), move(o.move+1)
> >>> + {}
> >>> +
> >>> + Tracker& operator=(Tracker) = delete;
> >>> +};
> >>> +
> >>> +constexpr
> >>> +void test_value_or()
> >>> +{
> >>> + Tracker t(100), u(200);
> >>> + std::optional<Tracker&> o(t);
> >>> +
> >>> + Tracker r1 = o.value_or(u);
> >>> + VERIFY( r1.copy == 101 );
> >>> + VERIFY( r1.move == 100 );
> >>> + Tracker r2 = o.value_or(std::move(u));
> >>> + VERIFY( r2.copy == 101 );
> >>> + VERIFY( r2.move == 100 );
> >>> + Tracker r3 = std::move(o).value_or(u);
> >>> + VERIFY( r3.copy == 101 );
> >>> + VERIFY( r3.move == 100 );
> >>> + Tracker r4 = std::move(o).value_or(std::move(u));
> >>> + VERIFY( r4.copy == 101 );
> >>> + VERIFY( r4.move == 100 );
> >>> +
> >>> + o.reset();
> >>> + Tracker r5 = o.value_or(u);
> >>> + VERIFY( r5.copy == 201 );
> >>> + VERIFY( r5.move == 200 );
> >>> + Tracker r6 = o.value_or(std::move(u));
> >>> + VERIFY( r6.copy == 200 );
> >>> + VERIFY( r6.move == 201 );
> >>> + Tracker r7 = std::move(o).value_or(u);
> >>> + VERIFY( r7.copy == 201 );
> >>> + VERIFY( r7.move == 200 );
> >>> + Tracker r8 = std::move(o).value_or(std::move(u));
> >>> + VERIFY( r8.copy == 200 );
> >>> + VERIFY( r8.move == 201 );
> >>> +}
> >>> +
> >>> +template<typename T>
> >>> +concept has_value_or_for = requires(std::optional<T&> t, T& u)
> >>> +{ t.value_or(u); };
> >>> +
> >>> +static_assert( has_value_or_for<int> );
> >>> +static_assert( has_value_or_for<NonMovable> );
> >>> +static_assert( !has_value_or_for<int[2]> );
> >>> +static_assert( has_value_or_for<int(*)[2]> );
> >>> +static_assert( !has_value_or_for<int()> );
> >>> +static_assert( has_value_or_for<int(*)()> );
> >>> +
> >>> +int i;
> >>> +NonMovable nm;
> >>> +int arr[2];
> >>> +int foo();
> >>> +
> >>> +int main()
> >>> +{
> >>> + auto test_all = [] {
> >>> + test_value<int>(i);
> >>> + test_value<NonMovable>(nm);
> >>> + test_value<int[2]>(arr);
> >>> + test_value<int()>(foo);
> >>> +
> >>> + test_value_or();
> >>> + return true;
> >>> + };
> >>> +
> >>> + test_all();
> >>> + static_assert(test_all());
> >>> +}
> >>> diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/assign.cc
> b/libstdc++-v3/testsuite/20_util/optional/ref/assign.cc
> >>> new file mode 100644
> >>> index 00000000000..be8b1c84eca
> >>> --- /dev/null
> >>> +++ b/libstdc++-v3/testsuite/20_util/optional/ref/assign.cc
> >>> @@ -0,0 +1,430 @@
> >>> +// { dg-do run { target c++26 } }
> >>> +
> >>> +#include <optional>
> >>> +#include <type_traits>
> >>> +#include <testsuite_hooks.h>
> >>> +
> >>> +struct NonTrivial
> >>> +{
> >>> + constexpr NonTrivial() {}
> >>> + constexpr NonTrivial(NonTrivial const&) {};
> >>> + constexpr ~NonTrivial() {};
> >>> +};
> >>> +
> >>> +struct NonMovable
> >>> +{
> >>> + NonMovable() = default;
> >>> + NonMovable(NonMovable&&) = delete;
> >>> +};
> >>> +
> >>> +template<typename T>
> >>> +struct Conv
> >>> +{
> >>> + T t;
> >>> +
> >>> + constexpr operator T() const noexcept
> >>> + { return t; }
> >>> +};
> >>> +
> >>> +struct Tracker
> >>> +{
> >>> + struct Counter
> >>> + {
> >>> + int copy;
> >>> + int move;
> >>> + };
> >>> +
> >>> + Counter ctor{0,0};
> >>> + Counter assign{0,0};
> >>> +
> >>> + Tracker() = default;
> >>> +
> >>> + constexpr Tracker(Tracker const& o)
> >>> + : ctor(o.ctor), assign(o.assign)
> >>> + {
> >>> + ctor.copy += 1;
> >>> + }
> >>> +
> >>> + constexpr Tracker(Tracker&& o)
> >>> + : ctor(o.ctor), assign(o.assign)
> >>> + {
> >>> + ctor.move += 1;
> >>> + }
> >>> +
> >>> + constexpr Tracker& operator=(const Tracker& o)
> >>> + {
> >>> + assign.copy += 1;
> >>> + return *this;
> >>> + }
> >>> +
> >>> + constexpr Tracker& operator=(Tracker&& o)
> >>> + {
> >>> + assign.move += 1;
> >>> + return *this;
> >>> + }
> >>> +
> >>> +};
> >>> +
> >>> +template<typename T>
> >>> +void
> >>> +test_trivial()
> >>> +{
> >>> + static_assert(std::is_copy_assignable_v<std::optional<T&>>);
> >>> + static_assert(std::is_move_assignable_v<std::optional<T&>>);
> >>> +}
> >>> +
> >>> +constexpr void
> >>> +test_trivial_all()
> >>> +{
> >>> + test_trivial<int>();
> >>> + test_trivial<NonTrivial>();
> >>> + test_trivial<std::optional<int&>>();
> >>> +}
> >>> +
> >>> +constexpr void
> >>> +test_copy()
> >>> +{
> >>> + Tracker t, u;
> >>> + std::optional<Tracker&> e;
> >>> + std::optional<Tracker&> o1(t);
> >>> + std::optional<Tracker&> o2(u);
> >>> +
> >>> + o2 = o1;
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &t );
> >>> + VERIFY( o2.has_value() );
> >>> + VERIFY( &o2.value() == &t );
> >>> + VERIFY( t.ctor.copy == 0 );
> >>> + VERIFY( t.ctor.move == 0 );
> >>> + VERIFY( t.assign.copy == 0 );
> >>> + VERIFY( t.assign.move == 0 );
> >>> +
> >>> + o2 = e;
> >>> + VERIFY( !o2.has_value() );
> >>> +
> >>> + o2 = std::move(o1);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &t );
> >>> + VERIFY( o2.has_value() );
> >>> + VERIFY( &o2.value() == &t );
> >>> + VERIFY( t.ctor.copy == 0 );
> >>> + VERIFY( t.ctor.move == 0 );
> >>> + VERIFY( t.assign.copy == 0 );
> >>> + VERIFY( t.assign.move == 0 );
> >>> +
> >>> + o2 = std::move(e);
> >>> + VERIFY( !o2.has_value() );
> >>> +}
> >>> +
> >>> +template<typename T, typename U>
> >>> +concept can_emplace = requires (T t, U&& u)
> >>> +{ t.emplace(std::forward<U>(u)); };
> >>> +
> >>> +constexpr void
> >>> +test_from_value()
> >>> +{
> >>> + NonTrivial v, u;
> >>> + const NonTrivial& cv = v;
> >>> + const std::optional<NonTrivial&> s(u);
> >>> + std::optional<NonTrivial&> o1;
> >>> + std::optional<const NonTrivial&> co1;
> >>> +
> >>> + o1 = s;
> >>> + o1 = v;
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v );
> >>> +
> >>> + o1.reset();
> >>> + VERIFY( !o1.has_value() );
> >>> +
> >>> + o1 = v;
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v );
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> + const NonTrivial&> );
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> + NonTrivial> );
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> + const NonTrivial> );
> >>> +
> >>> + o1 = s;
> >>> + o1.emplace(v);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v );
> >>> +
> >>> + o1 = std::nullopt;
> >>> + VERIFY( !o1.has_value() );
> >>> +
> >>> + o1.emplace(v);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v );
> >>> +
> >>> + static_assert( !can_emplace<std::optional<NonTrivial&>, const
> NonTrivial&> );
> >>> + static_assert( !can_emplace<std::optional<NonTrivial&>, NonTrivial>
> );
> >>> + static_assert( !can_emplace<std::optional<NonTrivial&>, const
> NonTrivial> );
> >>> +
> >>> + co1 = s;
> >>> + co1 = v;
> >>> + VERIFY( co1.has_value() );
> >>> + VERIFY( &co1.value() == &v );
> >>> +
> >>> + co1 = std::nullopt;
> >>> + co1 = cv;
> >>> + VERIFY( co1.has_value() );
> >>> + VERIFY( &co1.value() == &v );
> >>> + // No binding to rvalue
> >>> + static_assert( !std::is_assignable_v<std::optional<const
> NonTrivial&>,
> >>> + NonTrivial> );
> >>> + static_assert( !std::is_assignable_v<std::optional<const
> NonTrivial&>,
> >>> + const NonTrivial> );
> >>> +
> >>> + co1 = std::nullopt;
> >>> + co1.emplace(v);
> >>> + VERIFY( co1.has_value() );
> >>> + VERIFY( &co1.value() == &v );
> >>> +
> >>> + co1 = s;
> >>> + co1.emplace(cv);
> >>> + VERIFY( co1.has_value() );
> >>> + VERIFY( &co1.value() == &v );
> >>> + // No binding to rvalue
> >>> + static_assert( !can_emplace<std::optional<const NonTrivial&>, const
> NonTrivial> );
> >>> + static_assert( !can_emplace<std::optional<const NonTrivial&>,
> NonTrivial> );
> >>> +
> >>> +
> >>> + // Conversion create a pr-value that would bind to temporary
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> + Conv<NonTrivial>&> );
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> + const Conv<NonTrivial>&> );
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> + Conv<NonTrivial>> );
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> + const Conv<NonTrivial>> );
> >>> +
> >>> + static_assert( !can_emplace<std::optional<NonTrivial&>,
> Conv<NonTrivial>&> );
> >>> + static_assert( !can_emplace<std::optional<NonTrivial&>, const
> Conv<NonTrivial>&> );
> >>> + static_assert( !can_emplace<std::optional<NonTrivial&>,
> Conv<NonTrivial>> );
> >>> + static_assert( !can_emplace<std::optional<NonTrivial&>, const
> Conv<NonTrivial>> );
> >>> +
> >>> + Conv<NonTrivial&> rw(v);
> >>> + const Conv<NonTrivial&> crw(v);
> >>> +
> >>> + o1 = std::nullopt;
> >>> + o1 = rw;
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v );
> >>> + o1 = s;
> >>> + o1 = crw;
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v );
> >>> + o1 = s;
> >>> + o1 = std::move(rw);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v );
> >>> + o1 = std::nullopt;
> >>> + o1 = std::move(crw);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v );
> >>> +
> >>> + o1 = s;
> >>> + o1.emplace(rw);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v );
> >>> + o1 = std::nullopt;
> >>> + o1.emplace(crw);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v );
> >>> + o1 = std::nullopt;
> >>> + o1.emplace(std::move(rw));
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v );
> >>> + o1 = s;
> >>> + o1.emplace(std::move(crw));
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v );
> >>> +}
> >>> +
> >>> +constexpr void
> >>> +test_from_opt_value()
> >>> +{
> >>> + NonTrivial u;
> >>> + std::optional<NonTrivial> v(std::in_place);
> >>> + const std::optional<NonTrivial>& cv = v;
> >>> +
> >>> + const std::optional<NonTrivial&> s(u);
> >>> + std::optional<NonTrivial&> o1;
> >>> + std::optional<const NonTrivial&> co1;
> >>> +
> >>> + o1 = s;
> >>> + o1 = v;
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v.value() );
> >>> + o1 = std::nullopt;
> >>> + o1 = v;
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v.value() );
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> + const
> std::optional<NonTrivial>&> );
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> + std::optional<NonTrivial>> );
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> + const
> std::optional<NonTrivial>> );
> >>> +
> >>> + co1 = s;
> >>> + co1 = v;
> >>> + VERIFY( co1.has_value() );
> >>> + VERIFY( &co1.value() == &v.value() );
> >>> + co1 = std::nullopt;
> >>> + co1 = cv;
> >>> + VERIFY( co1.has_value() );
> >>> + VERIFY( &co1.value() == &v.value() );
> >>> + // No binding to rvalue
> >>> + static_assert( !std::is_assignable_v<std::optional<const
> NonTrivial&>,
> >>> + std::optional<NonTrivial>> );
> >>> + static_assert( !std::is_assignable_v<std::optional<const
> NonTrivial&>,
> >>> + const
> std::optional<NonTrivial>> );
> >>> +
> >>> + // Conversion create a pr-value that would bind to temporary
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> +
> std::optional<Conv<NonTrivial>>&> );
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> + const
> std::optional<Conv<NonTrivial>>&> );
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> +
> std::optional<Conv<NonTrivial>>> );
> >>> + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>,
> >>> + const
> std::optional<Conv<NonTrivial>>> );
> >>> +
> >>> + std::optional<Conv<NonTrivial&>> rw(*v);
> >>> + std::optional<const Conv<NonTrivial&>> crw(*v);
> >>> +
> >>> + o1 = std::nullopt;
> >>> + o1 = rw;
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v.value() );
> >>> + o1 = s;
> >>> + o1 = crw;
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v.value() );
> >>> + o1 = s;
> >>> + o1 = std::move(rw);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v.value() );
> >>> + o1 = std::nullopt;
> >>> + o1 = std::move(crw);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v.value() );
> >>> +}
> >>> +
> >>> +constexpr void
> >>> +test_to_opt_value()
> >>> +{
> >>> + Tracker t;
> >>> + std::optional<Tracker&> er;
> >>> + std::optional<Tracker&> r(t);
> >>> + const std::optional<Tracker&> cr(t);
> >>> +
> >>> + std::optional<Tracker> o1;
> >>> + o1 = r;
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( o1->ctor.copy == 1 );
> >>> + VERIFY( o1->ctor.move == 0 );
> >>> + VERIFY( o1->assign.copy == 0 );
> >>> + VERIFY( o1->assign.move == 0 );
> >>> +
> >>> + o1 = r;
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( o1->ctor.copy == 1 );
> >>> + VERIFY( o1->ctor.move == 0 );
> >>> + VERIFY( o1->assign.copy == 1 );
> >>> + VERIFY( o1->assign.move == 0 );
> >>> +
> >>> + o1 = er;
> >>> + VERIFY( !o1.has_value() );
> >>> +
> >>> + o1 = cr;
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( o1->ctor.copy == 1 );
> >>> + VERIFY( o1->ctor.move == 0 );
> >>> + VERIFY( o1->assign.copy == 0 );
> >>> + VERIFY( o1->assign.move == 0 );
> >>> +
> >>> + o1 = cr;
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( o1->ctor.copy == 1 );
> >>> + VERIFY( o1->ctor.move == 0 );
> >>> + VERIFY( o1->assign.copy == 1 );
> >>> + VERIFY( o1->assign.move == 0 );
> >>> +
> >>> + o1 = std::move(er);
> >>> +
> >>> + o1 = std::move(r);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( o1->ctor.copy == 1 );
> >>> + VERIFY( o1->ctor.move == 0 );
> >>> + VERIFY( o1->assign.copy == 0 );
> >>> + VERIFY( o1->assign.move == 0 );
> >>> +
> >>> + o1 = std::move(cr);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( o1->ctor.copy == 1 );
> >>> + VERIFY( o1->ctor.move == 0 );
> >>> + VERIFY( o1->assign.copy == 1 );
> >>> + VERIFY( o1->assign.move == 0 );
> >>> +}
> >>> +
> >>> +constexpr void
> >>> +test_swap()
> >>> +{
> >>> + NonMovable a, b;
> >>> + std::optional<NonMovable&> oa(a), ob(b);
> >>> +
> >>> + oa.swap(ob);
> >>> + static_assert(noexcept(oa.swap(ob)));
> >>> + VERIFY( oa.has_value() );
> >>> + VERIFY( &oa.value() == &b );
> >>> + VERIFY( ob.has_value() );
> >>> + VERIFY( &ob.value() == &a );
> >>> +
> >>> + swap(oa, ob);
> >>> +
> static_assert(std::is_nothrow_swappable_v<std::optional<NonMovable&>>);
> >>> + VERIFY( oa.has_value() );
> >>> + VERIFY( &oa.value() == &a );
> >>> + VERIFY( ob.has_value() );
> >>> + VERIFY( &ob.value() == &b );
> >>> +
> >>> + ob.reset();
> >>> + oa.swap(ob);
> >>> + VERIFY( !oa.has_value() );
> >>> + VERIFY( ob.has_value() );
> >>> + VERIFY( &ob.value() == &a );
> >>> +
> >>> + ob.reset();
> >>> + std::swap(oa, ob);
> >>> + VERIFY( !oa.has_value() );
> >>> + VERIFY( !ob.has_value() );
> >>> +
> >>> + std::optional<const NonMovable&> ca(a), cb(b);
> >>> + swap(ca, cb);
> >>> + VERIFY( ca.has_value() );
> >>> + VERIFY( &ca.value() == &b );
> >>> + VERIFY( cb.has_value() );
> >>> + VERIFY( &cb.value() == &a );
> >>> +
> >>> + static_assert(!std::is_swappable_with_v<std::optional<int>&,
> std::optional<int&>&>);
> >>> +}
> >>> +
> >>> +int main()
> >>> +{
> >>> + auto test_all = [] {
> >>> + test_copy();
> >>> + test_from_value();
> >>> + test_from_opt_value();
> >>> + test_to_opt_value();
> >>> + test_swap();
> >>> + return true;
> >>> + };
> >>> +
> >>> + test_all();
> >>> + static_assert(test_all());
> >>> +}
> >>> diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/cons.cc
> b/libstdc++-v3/testsuite/20_util/optional/ref/cons.cc
> >>> new file mode 100644
> >>> index 00000000000..b13e8b92329
> >>> --- /dev/null
> >>> +++ b/libstdc++-v3/testsuite/20_util/optional/ref/cons.cc
> >>> @@ -0,0 +1,356 @@
> >>> +// { dg-do run { target c++26 } }
> >>> +
> >>> +#include <optional>
> >>> +#include <type_traits>
> >>> +#include <testsuite_hooks.h>
> >>> +
> >>> +struct NonTrivial
> >>> +{
> >>> + constexpr NonTrivial() {}
> >>> + constexpr NonTrivial(NonTrivial const&) {};
> >>> + constexpr ~NonTrivial() {};
> >>> +};
> >>> +
> >>> +struct NonMovable
> >>> +{
> >>> + constexpr NonMovable() {}
> >>> + NonMovable(NonMovable&&) = delete;
> >>> +};
> >>> +
> >>> +template<typename T>
> >>> +struct Conv
> >>> +{
> >>> + T t;
> >>> +
> >>> + constexpr operator T() const noexcept
> >>> + { return t; }
> >>> +};
> >>> +
> >>> +struct Tracker
> >>> +{
> >>> + int copy = 0;
> >>> + int move = 0;
> >>> +
> >>> + Tracker() = default;
> >>> +
> >>> + constexpr Tracker(Tracker const& o)
> >>> + : copy(o.copy+1), move(o.move)
> >>> + {}
> >>> +
> >>> + constexpr Tracker(Tracker&& o)
> >>> + : copy(o.copy), move(o.move+1)
> >>> + {}
> >>> +
> >>> + Tracker& operator=(Tracker) = delete;
> >>> +};
> >>> +
> >>> +template<typename T>
> >>> +void
> >>> +test_trivial()
> >>> +{
> >>> + static_assert(std::is_trivially_copyable_v<std::optional<T&>>);
> >>> + static_assert(std::is_copy_constructible_v<std::optional<T&>>);
> >>> + static_assert(std::is_move_constructible_v<std::optional<T&>>);
> >>> + static_assert(std::is_destructible_v<std::optional<T&>>);
> >>> +}
> >>> +
> >>> +constexpr void
> >>> +test_trivial_all()
> >>> +{
> >>> + test_trivial<int>();
> >>> + test_trivial<NonTrivial>();
> >>> + test_trivial<NonMovable>();
> >>> + test_trivial<std::optional<int&>>();
> >>> +}
> >>> +
> >>> +constexpr void
> >>> +test_copy()
> >>> +{
> >>> + Tracker t;
> >>> + std::optional<Tracker&> o1(t);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &t );
> >>> + VERIFY( t.copy == 0 );
> >>> + VERIFY( t.move == 0 );
> >>> +
> >>> + std::optional<Tracker&> o2(o1);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &t );
> >>> + VERIFY( o2.has_value() );
> >>> + VERIFY( &o2.value() == &t );
> >>> + VERIFY( t.copy == 0 );
> >>> + VERIFY( t.move == 0 );
> >>> +
> >>> + std::optional<Tracker&> o3(std::move(o1));
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &t );
> >>> + VERIFY( o3.has_value() );
> >>> + VERIFY( &o3.value() == &t );
> >>> + VERIFY( t.copy == 0 );
> >>> + VERIFY( t.move == 0 );
> >>> +
> >>> + std::optional<Tracker&> e;
> >>> + VERIFY( !e.has_value() );
> >>> +
> >>> + std::optional<Tracker&> o4(e);
> >>> + VERIFY( !e.has_value() );
> >>> + VERIFY( !o4.has_value() );
> >>> +
> >>> + std::optional<Tracker&> o5(std::move(e));
> >>> + VERIFY( !e.has_value() );
> >>> + VERIFY( !o5.has_value() );
> >>> +}
> >>> +
> >>> +
> >>> +constexpr void
> >>> +test_from_value()
> >>> +{
> >>> + NonTrivial v;
> >>> + const NonTrivial& cv = v;
> >>> +
> >>> + std::optional<NonTrivial&> o1(v);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + const NonTrivial&> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + NonTrivial> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + const NonTrivial> );
> >>> +
> >>> + std::optional<NonTrivial&> o2(std::in_place, v);
> >>> + VERIFY( o2.has_value() );
> >>> + VERIFY( &o2.value() == &v );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + std::in_place_t, const
> NonTrivial&> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + std::in_place_t, NonTrivial>
> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + std::in_place_t, const
> NonTrivial> );
> >>> +
> >>> + std::optional<const NonTrivial&> co1(v);
> >>> + VERIFY( co1.has_value() );
> >>> + VERIFY( &co1.value() == &v );
> >>> + std::optional<const NonTrivial&> co2(cv);
> >>> + VERIFY( co2.has_value() );
> >>> + VERIFY( &co2.value() == &v );
> >>> + // No binding to rvalue
> >>> + static_assert( !std::is_constructible_v<std::optional<const
> NonTrivial&>,
> >>> + NonTrivial> );
> >>> + static_assert( !std::is_constructible_v<std::optional<const
> NonTrivial&>,
> >>> + const NonTrivial> );
> >>> +
> >>> + std::optional<const NonTrivial&> co3(std::in_place, v);
> >>> + VERIFY( co3.has_value() );
> >>> + VERIFY( &co3.value() == &v );
> >>> + std::optional<const NonTrivial&> co4(std::in_place, cv);
> >>> + VERIFY( co4.has_value() );
> >>> + VERIFY( &co4.value() == &v );
> >>> + // No binding to rvalue
> >>> + static_assert( !std::is_constructible_v<std::optional<const
> NonTrivial&>,
> >>> + std::in_place_t, NonTrivial>
> );
> >>> + static_assert( !std::is_constructible_v<std::optional<const
> NonTrivial&>,
> >>> + std::in_place_t, const
> NonTrivial> );
> >>> +
> >>> + // Conversion create a pr-value that would bind to temporary
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + Conv<NonTrivial>&> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + const Conv<NonTrivial>&> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + Conv<NonTrivial>> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + const Conv<NonTrivial>> );
> >>> +
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + std::in_place_t,
> Conv<NonTrivial>&> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + std::in_place_t, const
> Conv<NonTrivial>&> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + std::in_place_t,
> Conv<NonTrivial>> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + std::in_place_t, const
> Conv<NonTrivial>> );
> >>> +
> >>> + Conv<NonTrivial&> rw(v);
> >>> + const Conv<NonTrivial&> crw(v);
> >>> +
> >>> + std::optional<NonTrivial&> ro1(rw);
> >>> + VERIFY( ro1.has_value() );
> >>> + VERIFY( &ro1.value() == &v );
> >>> + std::optional<NonTrivial&> ro2(crw);
> >>> + VERIFY( ro2.has_value() );
> >>> + VERIFY( &ro2.value() == &v );
> >>> + std::optional<NonTrivial&> ro3(std::move(rw));
> >>> + VERIFY( ro3.has_value() );
> >>> + VERIFY( &ro3.value() == &v );
> >>> + std::optional<NonTrivial&> ro4(std::move(crw));
> >>> + VERIFY( ro4.has_value() );
> >>> + VERIFY( &ro4.value() == &v );
> >>> +
> >>> + std::optional<NonTrivial&> ro5(std::in_place, rw);
> >>> + VERIFY( ro5.has_value() );
> >>> + VERIFY( &ro5.value() == &v );
> >>> + std::optional<NonTrivial&> ro6(std::in_place, crw);
> >>> + VERIFY( ro6.has_value() );
> >>> + VERIFY( &ro6.value() == &v );
> >>> + std::optional<NonTrivial&> ro7(std::in_place, std::move(rw));
> >>> + VERIFY( ro7.has_value() );
> >>> + VERIFY( &ro7.value() == &v );
> >>> + std::optional<NonTrivial&> ro8(std::in_place, std::move(crw));
> >>> + VERIFY( ro8.has_value() );
> >>> + VERIFY( &ro8.value() == &v );
> >>> +}
> >>> +
> >>> +constexpr void
> >>> +test_from_opt_value()
> >>> +{
> >>> + std::optional<NonTrivial> v(std::in_place);
> >>> + const std::optional<NonTrivial>& cv = v;
> >>> +
> >>> + std::optional<NonTrivial&> o1(v);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &v.value() );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + const
> std::optional<NonTrivial>&> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + std::optional<NonTrivial>> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + const
> std::optional<NonTrivial>> );
> >>> +
> >>> + std::optional<const NonTrivial&> co1(v);
> >>> + VERIFY( co1.has_value() );
> >>> + VERIFY( &co1.value() == &v.value() );
> >>> + std::optional<const NonTrivial&> co2(cv);
> >>> + VERIFY( co2.has_value() );
> >>> + VERIFY( &co2.value() == &v.value() );
> >>> + // No binding to rvalue
> >>> + static_assert( !std::is_constructible_v<std::optional<const
> NonTrivial&>,
> >>> + std::optional<NonTrivial>> );
> >>> + static_assert( !std::is_constructible_v<std::optional<const
> NonTrivial&>,
> >>> + const
> std::optional<NonTrivial>> );
> >>> +
> >>> + // Conversion create a pr-value that would bind to temporary
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> +
> std::optional<Conv<NonTrivial>>&> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + const
> std::optional<Conv<NonTrivial>>&> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> +
> std::optional<Conv<NonTrivial>>> );
> >>> + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>,
> >>> + const
> std::optional<Conv<NonTrivial>>> );
> >>> +
> >>> + std::optional<Conv<NonTrivial&>> rw(*v);
> >>> + std::optional<const Conv<NonTrivial&>> crw(*v);
> >>> +
> >>> + std::optional<NonTrivial&> ro1(rw);
> >>> + VERIFY( ro1.has_value() );
> >>> + VERIFY( &ro1.value() == &v.value() );
> >>> + std::optional<NonTrivial&> ro2(crw);
> >>> + VERIFY( ro2.has_value() );
> >>> + VERIFY( &ro2.value() == &v.value() );
> >>> + std::optional<NonTrivial&> ro3(std::move(rw));
> >>> + VERIFY( ro3.has_value() );
> >>> + VERIFY( &ro3.value() == &v.value() );
> >>> + std::optional<NonTrivial&> ro4(std::move(crw));
> >>> + VERIFY( ro4.has_value() );
> >>> + VERIFY( &ro4.value() == &v.value() );
> >>> +}
> >>> +
> >>> +constexpr void
> >>> +test_to_opt_value()
> >>> +{
> >>> + Tracker t;
> >>> + std::optional<Tracker&> r(t);
> >>> + const std::optional<Tracker&> cr(t);
> >>> +
> >>> + std::optional<Tracker> o1(r);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( o1->copy == 1 );
> >>> + VERIFY( o1->move == 0 );
> >>> +
> >>> + std::optional<Tracker> o2(cr);
> >>> + VERIFY( o2.has_value() );
> >>> + VERIFY( o2->copy == 1 );
> >>> + VERIFY( o2->move == 0 );
> >>> +
> >>> + std::optional<Tracker> o3(std::move(r));
> >>> + VERIFY( o3.has_value() );
> >>> + VERIFY( o3->copy == 1 );
> >>> + VERIFY( o3->move == 0 );
> >>> +
> >>> + std::optional<Tracker> o4(std::move(cr));
> >>> + VERIFY( o4.has_value() );
> >>> + VERIFY( o4->copy == 1 );
> >>> + VERIFY( o4->move == 0 );
> >>> +
> >>> + std::optional<Tracker&> er;
> >>> + const std::optional<Tracker&> cer;
> >>> +
> >>> + std::optional<Tracker> e1(er);
> >>> + VERIFY( !e1.has_value() );
> >>> +
> >>> + std::optional<Tracker> e2(cer);
> >>> + VERIFY( !e2.has_value() );
> >>> +
> >>> + std::optional<Tracker> e3(std::move(er));
> >>> + VERIFY( !e3.has_value() );
> >>> +
> >>> + std::optional<Tracker> e4(std::move(cer));
> >>> + VERIFY( !e4.has_value() );
> >>> +}
> >>> +
> >>> +constexpr void
> >>> +test_opt_opt()
> >>> +{
> >>> + std::optional<int> s(43);
> >>> +
> >>> + std::optional<std::optional<int>&> o1(s);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &s );
> >>> +
> >>> + std::optional<std::optional<int>&> o2(std::in_place, s);
> >>> + VERIFY( o2.has_value() );
> >>> + VERIFY( &o2.value() == &s );
> >>> +
> >>> + std::optional<std::optional<int>> o3(o1);
> >>> + VERIFY( o2.has_value() );
> >>> + VERIFY( o2.value().has_value() );
> >>> + VERIFY( o2.value() == 43 );
> >>> +
> >>> + s.reset();
> >>> + std::optional<std::optional<int>&> o4(s);
> >>> + VERIFY( o4.has_value() );
> >>> + VERIFY( &o4.value() == &s );
> >>> +
> >>> + std::optional<std::optional<int>&> o5(std::in_place, s);
> >>> + VERIFY( o5.has_value() );
> >>> + VERIFY( &o5.value() == &s );
> >>> +
> >>> + std::optional<std::optional<int>> o6(o1);
> >>> + VERIFY( o6.has_value() );
> >>> + VERIFY( !o6.value().has_value() );
> >>> +
> >>> + std::optional<std::optional<int>> s2(std::in_place);
> >>> + std::optional<std::optional<int>&> oo1(s2);
> >>> + VERIFY( oo1.has_value() );
> >>> + VERIFY( &oo1.value() == &s2.value() );
> >>> +
> >>> + s2.reset();
> >>> + std::optional<std::optional<int>&> oo2(s2);
> >>> + VERIFY( !oo2.has_value() );
> >>> +}
> >>> +
> >>> +int main()
> >>> +{
> >>> + auto test_all = [] {
> >>> + test_copy();
> >>> + test_from_value();
> >>> + test_from_opt_value();
> >>> + test_to_opt_value();
> >>> + test_opt_opt();
> >>> + return true;
> >>> + };
> >>> +
> >>> + test_all();
> >>> + static_assert(test_all());
> >>> +}
> >>> diff --git
> a/libstdc++-v3/testsuite/20_util/optional/ref/internal_traits.cc
> b/libstdc++-v3/testsuite/20_util/optional/ref/internal_traits.cc
> >>> new file mode 100644
> >>> index 00000000000..ef50da7e9fa
> >>> --- /dev/null
> >>> +++ b/libstdc++-v3/testsuite/20_util/optional/ref/internal_traits.cc
> >>> @@ -0,0 +1,11 @@
> >>> +// { dg-do compile { target c++26 } }
> >>> +
> >>> +#include <optional>
> >>> +#include <variant>
> >>> +
> >>> +template<typename T>
> >>> +constexpr bool _Never_valueless
> >>> + = std::__detail::__variant::_Never_valueless_alt<T>::value;
> >>> +
> >>> +static_assert( _Never_valueless<std::optional<int&>> );
> >>> +static_assert( _Never_valueless<std::optional<const int&>> );
> >>> diff --git
> a/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/1.cc
> b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/1.cc
> >>> new file mode 100644
> >>> index 00000000000..d9946d034ef
> >>> --- /dev/null
> >>> +++ b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/1.cc
> >>> @@ -0,0 +1,74 @@
> >>> +// { dg-do run { target c++20 } }
> >>> +
> >>> +#include <optional>
> >>> +#include <utility>
> >>> +#include <testsuite_hooks.h>
> >>> +
> >>> +struct NonTrivial
> >>> +{
> >>> + constexpr NonTrivial() {}
> >>> + constexpr NonTrivial(NonTrivial const&) {};
> >>> + constexpr ~NonTrivial() {};
> >>> +};
> >>> +
> >>> +template<typename T>
> >>> +struct Conv
> >>> +{
> >>> + T t;
> >>> +
> >>> + constexpr operator T() const noexcept
> >>> + { return t; }
> >>> +};
> >>> +
> >>> +constexpr bool test()
> >>> +{
> >>> + NonTrivial t;
> >>> + const NonTrivial& ct = t;
> >>> +
> >>> +#if __cplusplus > 202302
> >>> + auto o1 = std::make_optional<NonTrivial&>(t);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() == &t );
> >>> +
> >>> + auto o2 = std::make_optional<const NonTrivial&>(t);
> >>> + VERIFY( o2.has_value() );
> >>> + VERIFY( &o2.value() == &t );
> >>> +
> >>> + auto o3 = std::make_optional<const NonTrivial&>(ct);
> >>> + VERIFY( o3.has_value() );
> >>> + VERIFY( &o3.value() == &t );
> >>> +
> >>> + Conv<NonTrivial&> rw(t);
> >>> + auto o4 = std::make_optional<NonTrivial&>(rw);
> >>> + VERIFY( o4.has_value() );
> >>> + VERIFY( &o4.value() == &t );
> >>> +
> >>> + auto o5 = std::make_optional<NonTrivial&>(std::as_const(rw));
> >>> + VERIFY( o5.has_value() );
> >>> + VERIFY( &o5.value() == &t );
> >>> +
> >>> + auto o6 = std::make_optional<NonTrivial&>(Conv<NonTrivial&>(t));
> >>> + VERIFY( o6.has_value() );
> >>> + VERIFY( &o6.value() == &t );
> >>> +#else
> >>> + auto o1 = std::make_optional<NonTrivial&>(t);
> >>> + VERIFY( o1.has_value() );
> >>> + VERIFY( &o1.value() != &t );
> >>> +
> >>> + auto o3 = std::make_optional<const NonTrivial&>(ct);
> >>> + VERIFY( o3.has_value() );
> >>> + VERIFY( &o3.value() != &t );
> >>> +
> >>> + auto o2 = std::make_optional<NonTrivial&&>(std::move(t));
> >>> + VERIFY( o2.has_value() );
> >>> + VERIFY( &o2.value() != &t );
> >>> +#endif
> >>> +
> >>> + return true;
> >>> +}
> >>> +
> >>> +int main()
> >>> +{
> >>> + test();
> >>> + static_assert(test());
> >>> +}
> >>> diff --git
> a/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_args_neg.cc
> b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_args_neg.cc
> >>> new file mode 100644
> >>> index 00000000000..6166c658c54
> >>> --- /dev/null
> >>> +++
> b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_args_neg.cc
> >>> @@ -0,0 +1,43 @@
> >>> +// { dg-do compile { target c++17 } }
> >>> +
> >>> +#include <optional>
> >>> +
> >>> +struct C {
> >>> + C();
> >>> + C(int);
> >>> +};
> >>> +C s(10);
> >>> +const C cs(1);
> >>> +
> >>> +template<typename T>
> >>> +using decay_pre26 =
> >>> +#if __cplusplus > 202302
> >>> + T;
> >>> +#else
> >>> + std::decay_t<T>;
> >>> +#endif
> >>> +
> >>> +auto z1 = std::make_optional<C&>(); // { dg-error "no matching
> function for call" }
> >>> +auto z2 = std::make_optional<const C&>(); // { dg-error "no matching
> function for call" }
> >>> +auto z3 = std::make_optional<C&&>(); // { dg-error "no matching
> function for call" }
> >>> +auto z4 = std::make_optional<const C&&>(); // { dg-error "no matching
> function for call" }
> >>> +
> >>> +auto o1 = std::make_optional<C&>(10); // { dg-error "no
> matching function for call" }
> >>> +auto o2 = std::make_optional<const C&>(10); // { dg-error "from
> here" }
> >>> +auto o3 = std::make_optional<C&&>(10); // { dg-error "from
> here" }
> >>> +auto o4 = std::make_optional<const C&&>(10); // { dg-error "from
> here" }
> >>> +
> >>> +auto t1 = std::make_optional<C&>(10, 20); // { dg-error "no
> matching function for call" }
> >>> +auto t2 = std::make_optional<const C&>(10, 20); // { dg-error "no
> matching function for call" }
> >>> +auto t3 = std::make_optional<C&&>(10, 20); // { dg-error "no
> matching function for call" }
> >>> +auto t3 = std::make_optional<const C&&>(10, 20); // { dg-error "no
> matching function for call" }
> >>> +
> >>> +// { dg-prune-output "no type named 'type' in 'struct std::enable_if"
> }
> >>> +// { dg-prune-output "type/value mismatch at argument 1 in template
> parameter list" }
> >>> +// { dg-prune-output "in a union may not have reference type" }
> >>> +// { dg-prune-output "static assertion failed" }
> >>> +// { dg-prune-output "forming pointer to reference type" }
> >>> +// { dg-prune-output "cannot bind .* reference of type" }
> >>> +// { dg-prune-output "binding reference of type" }
> >>> +// { dg-prune-output "no matching function for call to
> 'std::optional" }
> >>> +
> >>> diff --git
> a/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_lvalue_neg.cc
> b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_lvalue_neg.cc
> >>> new file mode 100644
> >>> index 00000000000..aed6791a3ff
> >>> --- /dev/null
> >>> +++
> b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_lvalue_neg.cc
> >>> @@ -0,0 +1,40 @@
> >>> +// { dg-do compile { target c++17 } }
> >>> +
> >>> +#include <optional>
> >>> +#include <type_traits>
> >>> +
> >>> +struct C {
> >>> + C();
> >>> + C(int);
> >>> +};
> >>> +C s(10);
> >>> +const C cs(1);
> >>> +
> >>> +template<typename T>
> >>> +using decay_pre26 =
> >>> +#if __cplusplus > 202302
> >>> + T;
> >>> +#else
> >>> + std::decay_t<T>;
> >>> +#endif
> >>> +
> >>> +auto lr1 = std::make_optional<C&>(s); // changed meaning
> >>> +static_assert( std::is_same_v< decltype(lr1),
> std::optional<decay_pre26<C&>>> );
> >>> +auto lr2 = std::make_optional<const C&>(s); // { dg-error "here" ""
> { target c++23_down } }
> >>> +auto lr3 = std::make_optional<C&&>(s); // { dg-error "no
> matching function for call" }
> >>> +auto lr4 = std::make_optional<const C&&>(s); // { dg-error "no
> matching function for call" }
> >>> +
> >>> +auto clr1 = std::make_optional<C&>(cs); // { dg-error "no
> matching function for call" }
> >>> +auto clr2 = std::make_optional<const C&>(cs); // changed meaning
> >>> +static_assert( std::is_same_v< decltype(clr2),
> std::optional<decay_pre26<const C&>>> );
> >>> +auto clr3 = std::make_optional<C&&>(cs); // { dg-error "no
> matching function for call" }
> >>> +auto clr3 = std::make_optional<const C&&>(cs); // { dg-error "no
> matching function for call" }
> >>> +
> >>> +// { dg-prune-output "no type named 'type' in 'struct std::enable_if"
> }
> >>> +// { dg-prune-output "type/value mismatch at argument 1 in template
> parameter list" }
> >>> +// { dg-prune-output "in a union may not have reference type" }
> >>> +// { dg-prune-output "static assertion failed" }
> >>> +// { dg-prune-output "forming pointer to reference type" }
> >>> +// { dg-prune-output "cannot bind .* reference of type" }
> >>> +// { dg-prune-output "binding reference of type" }
> >>> +// { dg-prune-output "no matching function for call to
> `std::optional" }
> >>> diff --git
> a/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_rvalue_neg.cc
> b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_rvalue_neg.cc
> >>> new file mode 100644
> >>> index 00000000000..22f669ceb68
> >>> --- /dev/null
> >>> +++
> b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_rvalue_neg.cc
> >>> @@ -0,0 +1,38 @@
> >>> +// { dg-do compile { target c++17 } }
> >>> +
> >>> +#include <optional>
> >>> +#include <type_traits>
> >>> +
> >>> +struct C {
> >>> + C();
> >>> + C(int);
> >>> +};
> >>> +C s(10);
> >>> +const C cs(1);
> >>> +
> >>> +template<typename T>
> >>> +using decay_pre26 =
> >>> +#if __cplusplus > 202302
> >>> + T;
> >>> +#else
> >>> + std::decay_t<T>;
> >>> +#endif
> >>> +
> >>> +auto p1 = std::make_optional<C&>(C(10)); // { dg-error "no
> matching function for call" }
> >>> +auto p2 = std::make_optional<const C&>(C(10)); // { dg-error "from
> here" }
> >>> +auto p3 = std::make_optional<C&&>(C(10)); // { dg-error "from
> here" "" { target c++26 } }
> >>> +auto p4 = std::make_optional<const C&&>(C(10)); // { dg-error "from
> here" }
> >>> +
> >>> +auto b1 = std::make_optional<C&>({10}); // { dg-error "no
> matching function for call" }
> >>> +auto b2 = std::make_optional<const C&>({10}); // { dg-error "no
> matching function for call" "" { target c++26 } }
> >>> +auto b3 = std::make_optional<C&&>({10}); // { dg-error "no
> matching function for call" "" { target c++26 } }
> >>> +auto b4 = std::make_optional<const C&&>({10}); // { dg-error "no
> matching function for call" "" { target c++26 } }
> >>> +
> >>> +// { dg-prune-output "no type named 'type' in 'struct std::enable_if"
> }
> >>> +// { dg-prune-output "type/value mismatch at argument 1 in template
> parameter list" }
> >>> +// { dg-prune-output "in a union may not have reference type" }
> >>> +// { dg-prune-output "static assertion failed" }
> >>> +// { dg-prune-output "forming pointer to reference type" }
> >>> +// { dg-prune-output "cannot bind .* reference of type" }
> >>> +// { dg-prune-output "binding reference of type" }
> >>> +// { dg-prune-output "no matching function for call to
> 'std::optional" }
> >>> diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/monadic.cc
> b/libstdc++-v3/testsuite/20_util/optional/ref/monadic.cc
> >>> new file mode 100644
> >>> index 00000000000..e8460c6528b
> >>> --- /dev/null
> >>> +++ b/libstdc++-v3/testsuite/20_util/optional/ref/monadic.cc
> >>> @@ -0,0 +1,192 @@
> >>> +// { dg-do run { target c++26 } }
> >>> +
> >>> +#include <optional>
> >>> +#include <type_traits>
> >>> +#include <testsuite_hooks.h>
> >>> +#include <utility>
> >>> +
> >>> +struct NonMovable
> >>> +{
> >>> + constexpr NonMovable() {}
> >>> + NonMovable(NonMovable&&) = delete;
> >>> +};
> >>> +
> >>> +struct Tracker
> >>> +{
> >>> + int copy = 0;
> >>> + int move = 0;
> >>> +
> >>> + Tracker() = default;
> >>> +
> >>> + constexpr Tracker(Tracker const& o)
> >>> + : copy(o.copy+1), move(o.move)
> >>> + {}
> >>> +
> >>> + constexpr Tracker(Tracker&& o)
> >>> + : copy(o.copy), move(o.move+1)
> >>> + {}
> >>> +
> >>> + Tracker& operator=(Tracker) = delete;
> >>> +
> >>> + void reset() {
> >>> + copy = move = 0;
> >>> + }
> >>> +};
> >>> +
> >>> +template<typename T>
> >>> +auto identity_of = []<typename U>(U&& t) -> std::optional<T>
> >>> +{
> >>> + static_assert( std::is_same_v<T, U&&> );
> >>> + VERIFY( t.copy == 0 );
> >>> + VERIFY( t.move == 0 );
> >>> + return std::optional<T>(t);
> >>> +};
> >>> +
> >>> +constexpr void
> >>> +test_and_then()
> >>> +{
> >>> + std::optional<Tracker> t(std::in_place);
> >>> + std::optional<Tracker&> rt(t);
> >>> + std::optional<const Tracker&> rct(t);
> >>> +
> >>> + auto r1 = t.and_then(identity_of<Tracker&>);
> >>> + VERIFY( r1.has_value() );
> >>> + VERIFY( &r1.value() == &t.value() );
> >>> +
> >>> + auto r2 = rt.and_then(identity_of<Tracker&>);
> >>> + VERIFY( r2.has_value() );
> >>> + VERIFY( &r2.value() == &t.value() );
> >>> +
> >>> + std::as_const(rt).and_then(identity_of<Tracker&>);
> >>> + std::move(rt).and_then(identity_of<Tracker&>);
> >>> +
> >>> + auto r4 = rct.and_then(identity_of<const Tracker&>);
> >>> + VERIFY( r4.has_value() );
> >>> + VERIFY( &r4.value() == &t.value() );
> >>> +
> >>> + std::as_const(rct).and_then(identity_of<const Tracker&>);
> >>> + std::move(rct).and_then(identity_of<const Tracker&>);
> >>> +
> >>> + auto r5 = rt.and_then([](Tracker&) { return std::optional<int>(42);
> });
> >>> + static_assert( std::is_same_v<decltype(r5), std::optional<int>> );
> >>> + VERIFY( r5.has_value() );
> >>> + VERIFY( r5.value() == 42 );
> >>> +
> >>> + auto r6 = rct.and_then([](const Tracker&) { return
> std::optional<int>(); });
> >>> + static_assert( std::is_same_v<decltype(r6), std::optional<int>> );
> >>> + VERIFY( !r6.has_value() );
> >>> +
> >>> + rct.reset();
> >>> + auto r7 = rct.and_then([](const Tracker&) { VERIFY(false); return
> std::optional<int>(42); });
> >>> + static_assert( std::is_same_v<decltype(r7), std::optional<int>> );
> >>> + VERIFY( !r7.has_value() );
> >>> +
> >>> + rt.reset();
> >>> + auto r8 = rt.and_then([](Tracker&) { VERIFY(false); return
> std::optional<int>(); });
> >>> + static_assert( std::is_same_v<decltype(r8), std::optional<int>> );
> >>> + VERIFY( !r8.has_value() );
> >>> +}
> >>> +
> >>> +template<typename T>
> >>> +constexpr void
> >>> +test_or_else()
> >>> +{
> >>> + T t, u;
> >>> +
> >>> + std::optional<T&> ot(t);
> >>> + auto r1 = ot.or_else([&] { VERIFY(false); return
> std::optional<T&>(u); });
> >>> + VERIFY( &ot.value() == &t );
> >>> + VERIFY( r1.has_value() );
> >>> + VERIFY( &r1.value() == &t );
> >>> + auto r2 = std::move(ot).or_else([&] { VERIFY(false); return
> std::optional<T&>(); });
> >>> + VERIFY( &ot.value() == &t );
> >>> + VERIFY( r2.has_value() );
> >>> + VERIFY( &r2.value() == &t );
> >>> +
> >>> + ot.reset();
> >>> + auto r3 = ot.or_else([&] { return std::optional<T&>(u); });
> >>> + VERIFY( !ot.has_value() );
> >>> + VERIFY( r3.has_value() );
> >>> + VERIFY( &r3.value() == &u );
> >>> + auto r4 = std::move(ot).or_else([] { return std::optional<T&>(); });
> >>> + VERIFY( !ot.has_value() );
> >>> + VERIFY( !r4.has_value() );
> >>> +}
> >>> +
> >>> +constexpr void
> >>> +test_transform()
> >>> +{
> >>> + std::optional<Tracker> t(std::in_place);
> >>> +
> >>> + auto r1 = t.transform(&Tracker::copy);
> >>> + static_assert( std::is_same_v<decltype(r1), std::optional<int&>> );
> >>> + VERIFY( r1.has_value() );
> >>> + VERIFY( &r1.value() == &t->copy );
> >>> + auto r2 = std::as_const(t).transform(&Tracker::move);
> >>> + static_assert( std::is_same_v<decltype(r2), std::optional<const
> int&>> );
> >>> + VERIFY( r2.has_value() );
> >>> + VERIFY( &r2.value() == &t->move );
> >>> +
> >>> + std::optional<Tracker&> rt(t);
> >>> + auto r3 = rt.transform(&Tracker::copy);
> >>> + static_assert( std::is_same_v<decltype(r3), std::optional<int&>> );
> >>> + VERIFY( r3.has_value() );
> >>> + VERIFY( &r3.value() == &t->copy );
> >>> + auto r4 = std::as_const(rt).transform(&Tracker::copy);
> >>> + static_assert( std::is_same_v<decltype(r4), std::optional<int&>> );
> >>> + VERIFY( r4.has_value() );
> >>> + VERIFY( &r4.value() == &t->copy );
> >>> + auto r5 = std::move(rt).transform(&Tracker::copy);
> >>> + static_assert( std::is_same_v<decltype(r5), std::optional<int&>> );
> >>> + VERIFY( r5.has_value() );
> >>> + VERIFY( &r5.value() == &t->copy );
> >>> +
> >>> + auto r6 = rt.transform([] (Tracker& t) { return 10; });
> >>> + static_assert( std::is_same_v<decltype(r6), std::optional<int>> );
> >>> + VERIFY( r6.has_value() );
> >>> + VERIFY( &r6.value() != &t->copy );
> >>> + VERIFY( r6.value() == 10 );
> >>> +
> >>> + auto r7 = rt.transform([] (Tracker& t) { return NonMovable(); });
> >>> + static_assert( std::is_same_v<decltype(r7),
> std::optional<NonMovable>> );
> >>> + VERIFY( r7.has_value() );
> >>> +
> >>> + rt.reset();
> >>> + auto r8 = rt.transform([] (Tracker& t) { VERIFY(false); return 42;
> });
> >>> + static_assert( std::is_same_v<decltype(r8), std::optional<int>> );
> >>> + VERIFY( !r8.has_value() );
> >>> +
> >>> + std::optional<const Tracker&> crt(t);
> >>> + auto r9 = crt.transform(&Tracker::copy);
> >>> + static_assert( std::is_same_v<decltype(r9), std::optional<const
> int&>> );
> >>> + VERIFY( r9.has_value() );
> >>> + VERIFY( &r9.value() == &t->copy );
> >>> + auto r10 = std::as_const(crt).transform(&Tracker::copy);
> >>> + static_assert( std::is_same_v<decltype(r10), std::optional<const
> int&>> );
> >>> + VERIFY( r10.has_value() );
> >>> + VERIFY( &r10.value() == &t->copy );
> >>> + auto r11 = std::move(crt).transform(&Tracker::copy);
> >>> + static_assert( std::is_same_v<decltype(r11), std::optional<const
> int&>> );
> >>> + VERIFY( r11.has_value() );
> >>> + VERIFY( &r11.value() == &t->copy );
> >>> +
> >>> + crt.reset();
> >>> + auto r12 = rt.transform([] (Tracker& t) { VERIFY(false); return 42;
> });
> >>> + static_assert( std::is_same_v<decltype(r12), std::optional<int>> );
> >>> + VERIFY( !r12.has_value() );
> >>> +}
> >>> +
> >>> +int main()
> >>> +{
> >>> + auto test_all = [] {
> >>> + test_and_then();
> >>> + test_transform();
> >>> + test_or_else<Tracker>();
> >>> + test_or_else<const Tracker>();
> >>> + test_or_else<NonMovable>();
> >>> + return true;
> >>> + };
> >>> +
> >>> + test_all();
> >>> + static_assert(test_all());
> >>> +}
> >>> diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/relops.cc
> b/libstdc++-v3/testsuite/20_util/optional/ref/relops.cc
> >>> new file mode 100644
> >>> index 00000000000..e0b30c8e898
> >>> --- /dev/null
> >>> +++ b/libstdc++-v3/testsuite/20_util/optional/ref/relops.cc
> >>> @@ -0,0 +1,229 @@
> >>> +// { dg-do run { target c++26 } }
> >>> +
> >>> +#include <optional>
> >>> +#include <functional>
> >>> +#include <testsuite_hooks.h>
> >>> +#include <type_traits>
> >>> +
> >>> +template<typename T, typename H = std::hash<T>>
> >>> +constexpr bool has_disabled_hash
> >>> + = !std::is_default_constructible_v<H>
> >>> + && !std::is_copy_constructible_v<H>
> >>> + && !std::is_move_constructible_v<H>
> >>> + && !std::is_copy_assignable_v<H>
> >>> + && !std::is_move_assignable_v<H>;
> >>> +
> >>> +static_assert(has_disabled_hash<std::optional<int&>>);
> >>> +static_assert(has_disabled_hash<std::optional<const int&>>);
> >>> +
> >>> +template<typename T, typename V>
> >>> +constexpr void
> >>> +test_compare_val(V& l, V& h)
> >>> +{
> >>> + std::optional<T> t;
> >>> +
> >>> + VERIFY( !(t == l) );
> >>> + VERIFY( (t != l) );
> >>> + VERIFY( (t < l) );
> >>> + VERIFY( (t <= l) );
> >>> + VERIFY( !(t > l) );
> >>> + VERIFY( !(t >= l) );
> >>> + VERIFY( (t <=> l) < 0 );
> >>> +
> >>> + VERIFY( !(l == t) );
> >>> + VERIFY( (l != t) );
> >>> + VERIFY( !(l < t) );
> >>> + VERIFY( !(l <= t) );
> >>> + VERIFY( (l > t) );
> >>> + VERIFY( (l >= t) );
> >>> + VERIFY( (l <=> t) > 0 );
> >>> +
> >>> + t.emplace(l);
> >>> + VERIFY( (t == l) );
> >>> + VERIFY( !(t != l) );
> >>> + VERIFY( !(t < l) );
> >>> + VERIFY( (t <= l) );
> >>> + VERIFY( !(t > l) );
> >>> + VERIFY( (t >= l) );
> >>> + VERIFY( (t <=> l) == 0 );
> >>> +
> >>> + VERIFY( (l == t) );
> >>> + VERIFY( !(l != t) );
> >>> + VERIFY( !(l < t) );
> >>> + VERIFY( (l <= t) );
> >>> + VERIFY( !(l > t) );
> >>> + VERIFY( (l >= t) );
> >>> + VERIFY( (t <=> l) == 0 );
> >>> +
> >>> + t.emplace(h);
> >>> + VERIFY( !(t == l) );
> >>> + VERIFY( (t != l) );
> >>> + VERIFY( !(t < l) );
> >>> + VERIFY( !(t <= l) );
> >>> + VERIFY( (t > l) );
> >>> + VERIFY( (t >= l) );
> >>> + VERIFY( (t <=> l) > 0 );
> >>> +
> >>> + VERIFY( !(l == t) );
> >>> + VERIFY( (l != t) );
> >>> + VERIFY( (l < t) );
> >>> + VERIFY( (l <= t) );
> >>> + VERIFY( !(l > t) );
> >>> + VERIFY( !(l >= t) );
> >>> + VERIFY( (l <=> t) < 0 );
> >>> +}
> >>> +
> >>> +template<typename T, typename U, typename V>
> >>> +constexpr void
> >>> +test_compare_opts(V& l, V& h)
> >>> +{
> >>> + std::optional<T> t;
> >>> + std::optional<U> u;
> >>> +
> >>> + VERIFY( (t == u) );
> >>> + VERIFY( !(t != u) );
> >>> + VERIFY( !(t < u) );
> >>> + VERIFY( (t <= u) );
> >>> + VERIFY( !(t > u) );
> >>> + VERIFY( (t >= u) );
> >>> + VERIFY( (t <=> u) == 0 );
> >>> +
> >>> + t.emplace(l);
> >>> + VERIFY( !(t == u) );
> >>> + VERIFY( (t != u) );
> >>> + VERIFY( !(t < u) );
> >>> + VERIFY( !(t <= u) );
> >>> + VERIFY( (t > u) );
> >>> + VERIFY( (t >= u) );
> >>> + VERIFY( (t <=> u) > 0 );
> >>> +
> >>> + u.emplace(l);
> >>> + VERIFY( (t == u) );
> >>> + VERIFY( !(t != u) );
> >>> + VERIFY( !(t < u) );
> >>> + VERIFY( (t <= u) );
> >>> + VERIFY( !(t > u) );
> >>> + VERIFY( (t <= u) );
> >>> + VERIFY( (t <=> u) == 0 );
> >>> +
> >>> + u.emplace(h);
> >>> + VERIFY( !(t == u) );
> >>> + VERIFY( (t != u) );
> >>> + VERIFY( (t < u) );
> >>> + VERIFY( (t <= u) );
> >>> + VERIFY( !(t > u) );
> >>> + VERIFY( !(t >= u) );
> >>> + VERIFY( (t <=> u) < 0 );
> >>> +
> >>> + t.reset();
> >>> + u.emplace(l);
> >>> + VERIFY( !(t == u) );
> >>> + VERIFY( (t != u) );
> >>> + VERIFY( (t < u) );
> >>> + VERIFY( (t <= u) );
> >>> + VERIFY( !(t > u) );
> >>> + VERIFY( !(t >= u) );
> >>> + VERIFY( (t <=> u) < 0 );
> >>> +
> >>> + t.emplace(h);
> >>> + VERIFY( !(t == u) );
> >>> + VERIFY( (t != u) );
> >>> + VERIFY( !(t < u) );
> >>> + VERIFY( !(t <= u) );
> >>> + VERIFY( (t > u) );
> >>> + VERIFY( (t >= u) );
> >>> + VERIFY( (t <=> u) > 0 );
> >>> +}
> >>> +
> >>> +template<typename V>
> >>> +constexpr void
> >>> +test_compare(V l, V h)
> >>> +{
> >>> + test_compare_val<V&>(l, h);
> >>> + test_compare_val<const V&>(l, h);
> >>> +
> >>> + test_compare_opts<V&, V&>(l, h);
> >>> + test_compare_opts<V, V&>(l, h);
> >>> + test_compare_opts<V&, V>(l, h);
> >>> +
> >>> + test_compare_opts<const V&, const V&>(l, h);
> >>> + test_compare_opts<V, const V&>(l, h);
> >>> + test_compare_opts<const V&, V>(l, h);
> >>> +
> >>> + test_compare_opts<V&, const V&>(l, h);
> >>> + test_compare_opts<const V&, V&>(l, h);
> >>> +}
> >>> +
> >>> +struct TreeWay
> >>> +{
> >>> + int v;
> >>> + friend auto operator<=>(TreeWay, TreeWay) = default;
> >>> +};
> >>> +
> >>> +struct Other
> >>> +{
> >>> + int v;
> >>> +
> >>> + constexpr Other(int p) : v(p) {}
> >>> + constexpr Other(TreeWay p) : v(p.v) {}
> >>> +
> >>> + friend bool operator==(Other, Other) = default;
> >>> + friend auto operator<=>(Other, Other) = default;
> >>> +
> >>> + friend constexpr bool
> >>> + operator==(const Other& lhs, const TreeWay& rhs)
> >>> + { return lhs.v == rhs.v; }
> >>> +
> >>> + friend constexpr std::strong_ordering
> >>> + operator<=>(const Other& lhs, const TreeWay& rhs)
> >>> + { return lhs.v <=> rhs.v; }
> >>> +};
> >>> +
> >>> +constexpr void
> >>> +test_heterogeneus_cmp()
> >>> +{
> >>> + TreeWay l{10};
> >>> + Other h{20};
> >>> +
> >>> + std::optional<TreeWay&> t;
> >>> + std::optional<const Other&> u;
> >>> +
> >>> + VERIFY( (t == u) );
> >>> + VERIFY( !(t != u) );
> >>> + VERIFY( !(t < u) );
> >>> + VERIFY( (t <= u) );
> >>> + VERIFY( !(t > u) );
> >>> + VERIFY( (t >= u) );
> >>> + VERIFY( (t <=> u) == 0 );
> >>> +
> >>> + t.emplace(l);
> >>> + VERIFY( !(t == u) );
> >>> + VERIFY( (t != u) );
> >>> + VERIFY( !(t < u) );
> >>> + VERIFY( !(t <= u) );
> >>> + VERIFY( (t > u) );
> >>> + VERIFY( (t >= u) );
> >>> + VERIFY( (t <=> u) > 0 );
> >>> +
> >>> + u.emplace(h);
> >>> + VERIFY( !(t == u) );
> >>> + VERIFY( (t != u) );
> >>> + VERIFY( (t < u) );
> >>> + VERIFY( (t <= u) );
> >>> + VERIFY( !(t > u) );
> >>> + VERIFY( !(t >= u) );
> >>> + VERIFY( (t <=> u) < 0 );
> >>> +}
> >>> +
> >>> +int main()
> >>> +{
> >>> + auto test_all = [] {
> >>> + test_compare(2, 5);
> >>> + test_compare(TreeWay{11}, TreeWay{12});
> >>> + test_heterogeneus_cmp();
> >>> + return true;
> >>> + };
> >>> +
> >>> + test_all();
> >>> + static_assert(test_all());
> >>> +}
> >>> diff --git
> a/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc
> b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc
> >>> index 3e393257928..ec47552c475 100644
> >>> --- a/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc
> >>> +++ b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc
> >>> @@ -8,8 +8,8 @@
> >>> # error "Feature-test macro for constrained_equality has wrong value"
> >>> #endif
> >>>
> >>> -template<typename T, typename U = T>
> >>> -concept eq_comparable
> >>> +template<typename T, typename U>
> >>> +concept eq_comparable_impl
> >>> = requires (const std::optional<T>& t, const std::optional<U>& u) {
> >>> t == u;
> >>> *t == u;
> >>> @@ -17,7 +17,19 @@ concept eq_comparable
> >>> };
> >>>
> >>> template<typename T, typename U = T>
> >>> -concept ne_comparable
> >>> +concept eq_comparable =
> >>> + eq_comparable_impl<T, U>
> >>> +#if __cplusplus > 202302l
> >>> + && eq_comparable_impl<T&, U&>
> >>> + && eq_comparable_impl<T const&, U const&>
> >>> + && eq_comparable_impl<T const&, U&>
> >>> + && eq_comparable_impl<T, U const&>
> >>> + && eq_comparable_impl<T&, U>
> >>> +#endif
> >>> +;
> >>> +
> >>> +template<typename T, typename U>
> >>> +concept ne_comparable_impl
> >>> = requires (const std::optional<T>& t, const std::optional<U>& u) {
> >>> t != u;
> >>> *t != u;
> >>> @@ -25,7 +37,19 @@ concept ne_comparable
> >>> };
> >>>
> >>> template<typename T, typename U = T>
> >>> -concept lt_comparable
> >>> +concept ne_comparable =
> >>> + ne_comparable_impl<T, U>
> >>> +#if __cplusplus > 202302l
> >>> + && ne_comparable_impl<T&, U&>
> >>> + && ne_comparable_impl<T const&, U const&>
> >>> + && ne_comparable_impl<T const&, U&>
> >>> + && ne_comparable_impl<T, U const&>
> >>> + && ne_comparable_impl<T&, U>
> >>> +#endif
> >>> +;
> >>> +
> >>> +template<typename T, typename U>
> >>> +concept lt_comparable_impl
> >>> = requires (const std::optional<T>& t, const std::optional<U>& u) {
> >>> t < u;
> >>> *t < u;
> >>> @@ -33,7 +57,19 @@ concept lt_comparable
> >>> };
> >>>
> >>> template<typename T, typename U = T>
> >>> -concept le_comparable
> >>> +concept lt_comparable =
> >>> + lt_comparable_impl<T, U>
> >>> +#if __cplusplus > 202302l
> >>> + && lt_comparable_impl<T&, U&>
> >>> + && lt_comparable_impl<T const&, U const&>
> >>> + && lt_comparable_impl<T const&, U&>
> >>> + && lt_comparable_impl<T, U const&>
> >>> + && lt_comparable_impl<T&, U>
> >>> +#endif
> >>> +;
> >>> +
> >>> +template<typename T, typename U>
> >>> +concept le_comparable_impl
> >>> = requires (const std::optional<T>& t, const std::optional<U>& u) {
> >>> t <= u;
> >>> *t <= u;
> >>> @@ -41,7 +77,19 @@ concept le_comparable
> >>> };
> >>>
> >>> template<typename T, typename U = T>
> >>> -concept gt_comparable
> >>> +concept le_comparable =
> >>> + le_comparable_impl<T, U>
> >>> +#if __cplusplus > 202302l
> >>> + && le_comparable_impl<T&, U&>
> >>> + && le_comparable_impl<T const&, U const&>
> >>> + && le_comparable_impl<T const&, U&>
> >>> + && le_comparable_impl<T, U const&>
> >>> + && le_comparable_impl<T&, U>
> >>> +#endif
> >>> +;
> >>> +
> >>> +template<typename T, typename U>
> >>> +concept gt_comparable_impl
> >>> = requires (const std::optional<T>& t, const std::optional<U>& u) {
> >>> t > u;
> >>> *t > u;
> >>> @@ -49,13 +97,37 @@ concept gt_comparable
> >>> };
> >>>
> >>> template<typename T, typename U = T>
> >>> -concept ge_comparable
> >>> +concept gt_comparable =
> >>> + gt_comparable_impl<T, U>
> >>> +#if __cplusplus > 202302l
> >>> + && gt_comparable_impl<T&, U&>
> >>> + && gt_comparable_impl<T const&, U const&>
> >>> + && gt_comparable_impl<T const&, U&>
> >>> + && gt_comparable_impl<T, U const&>
> >>> + && gt_comparable_impl<T&, U>
> >>> +#endif
> >>> +;
> >>> +
> >>> +template<typename T, typename U>
> >>> +concept ge_comparable_impl
> >>> = requires (const std::optional<T>& t, const std::optional<U>& u) {
> >>> t >= u;
> >>> *t >= u;
> >>> t >= *u;
> >>> };
> >>>
> >>> +template<typename T, typename U = T>
> >>> +concept ge_comparable =
> >>> + ge_comparable_impl<T, U>
> >>> +#if __cplusplus > 202302l
> >>> + && ge_comparable_impl<T&, U&>
> >>> + && ge_comparable_impl<T const&, U const&>
> >>> + && ge_comparable_impl<T const&, U&>
> >>> + && ge_comparable_impl<T, U const&>
> >>> + && ge_comparable_impl<T&, U>
> >>> +#endif
> >>> +;
> >>> +
> >>> static_assert( eq_comparable<int> );
> >>> static_assert( ne_comparable<int> );
> >>> static_assert( lt_comparable<int> );
> >>> diff --git a/libstdc++-v3/testsuite/20_util/optional/requirements.cc
> b/libstdc++-v3/testsuite/20_util/optional/requirements.cc
> >>> index 68e59057b5e..9e8cf83da44 100644
> >>> --- a/libstdc++-v3/testsuite/20_util/optional/requirements.cc
> >>> +++ b/libstdc++-v3/testsuite/20_util/optional/requirements.cc
> >>> @@ -26,8 +26,10 @@
> >>> # error "Feature test macro for optional has wrong value in
> <optional>"
> >>> #elif __cplusplus == 202002L && __cpp_lib_optional != 202106L
> >>> # error "Feature test macro for optional has wrong value for C++20 in
> <optional>"
> >>> -#elif __cplusplus > 202002L && __cpp_lib_optional != 202110L
> >>> -# error "Feature test macro for optional has wrong value for C++23 in
> <version>"
> >>> +#elif __cplusplus == 202302L && __cpp_lib_optional != 202110L
> >>> +# error "Feature test macro for optional has wrong value for C++23 in
> <optional>"
> >>> +#elif __cplusplus > 202302L && __cpp_lib_optional != 202506L
> >>> +# error "Feature test macro for optional has wrong value for C++26 in
> <optional>"
> >>> #endif
> >>>
> >>> #include <testsuite_hooks.h>
> >>> diff --git
> a/libstdc++-v3/testsuite/20_util/optional/requirements_neg.cc
> b/libstdc++-v3/testsuite/20_util/optional/requirements_neg.cc
> >>> index 688c305803e..142fbbfc515 100644
> >>> --- a/libstdc++-v3/testsuite/20_util/optional/requirements_neg.cc
> >>> +++ b/libstdc++-v3/testsuite/20_util/optional/requirements_neg.cc
> >>> @@ -2,17 +2,32 @@
> >>>
> >>> #include <optional>
> >>>
> >>> +// C++ < 26:
> >>> // T shall be a type other than cv in_place_t or cv nullopt_t
> >>> // that meets the Cpp17Destructible requirements
> >>> +// C++ >= 26:
> >>> +// A type X is a valid contained type for optional if X is an lvalue
> reference
> >>> +// type or a complete non-array object type, and remove_cvref_t<X> is
> a type
> >>> +// other than in_place_t or nullopt_t. If a specialization of optional
> >>> +// is instantiated with a type T that is not a valid contained type
> for
> >>> +// optional, the program is ill-formed. If T is an object type,
> >>> +// T shall meet the Cpp17Destructible requirements
> >>>
> >>> std::optional<std::nullopt_t> o1; // { dg-error "here" }
> >>> std::optional<const std::nullopt_t> o2; // { dg-error "here" }
> >>> std::optional<std::in_place_t> o3; // { dg-error "here" }
> >>> std::optional<const std::in_place_t> o4; // { dg-error "here" }
> >>> -std::optional<int&> o5; // { dg-error "here" }
> >>> +std::optional<int&> o5; // { dg-error "here"
> "optional<T&> is a C++26 feature" { target c++23_down } }
> >>> std::optional<int[1]> o6; // { dg-error "here" }
> >>> std::optional<int[]> o7; // { dg-error "here" }
> >>> std::optional<int()> o8;
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20251021/3bf25275/attachment-0001.htm>
More information about the Libstdc++
mailing list