// Moved down here (after concepts) by topological sort.
ftms = {
name = optional;
- values = {
+ values = { // optional<T&>
+ v = 202506;
+ cxxmin = 26;
+ extra_cond = "__glibcxx_concepts";
+ };
+ values = { // monadic operations
v = 202110;
cxxmin = 23;
extra_cond = "__glibcxx_concepts";
};
- values = {
+ values = { // full constexpr support
v = 202106;
cxxmin = 20;
};
#undef __glibcxx_want_concepts
#if !defined(__cpp_lib_optional)
-# if (__cplusplus >= 202100L) && (__glibcxx_concepts)
+# if (__cplusplus > 202302L) && (__glibcxx_concepts)
+# 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
# define __cpp_lib_observable_checkpoint 202506L
# endif
# endif
-#endif /* !defined(__cpp_lib_observable_checkpoint) && defined(__glibcxx_want_observable_checkpoint) */
+#endif /* !defined(__cpp_lib_observable_checkpoint) */
#undef __glibcxx_want_observable_checkpoint
#if !defined(__cpp_lib_algorithm_default_value_type)
# 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.
*/
// 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>;
noexcept(is_nothrow_constructible_v<_Tp, const _Up&>)
{
if (__t)
- emplace(__t._M_get());
+ emplace(__t._M_fwd());
}
template<typename _Up>
noexcept(is_nothrow_constructible_v<_Tp, _Up>)
{
if (__t)
- emplace(std::move(__t._M_get()));
+ emplace(std::move(__t)._M_fwd());
}
template<typename... _Args>
noexcept(is_nothrow_constructible_v<_Tp, const _Up&>)
{
if (__t)
- emplace(__t._M_get());
+ emplace(__t._M_fwd());
}
template<typename _Up,
noexcept(is_nothrow_constructible_v<_Tp, const _Up&>)
{
if (__t)
- emplace(__t._M_get());
+ emplace(__t._M_fwd());
}
template<typename _Up,
noexcept(is_nothrow_constructible_v<_Tp, _Up>)
{
if (__t)
- emplace(std::move(__t._M_get()));
+ emplace(std::move(__t)._M_fwd());
}
template<typename _Up,
noexcept(is_nothrow_constructible_v<_Tp, _Up>)
{
if (__t)
- emplace(std::move(__t._M_get()));
+ emplace(std::move(__t)._M_fwd());
}
template<typename... _Args,
if (__u)
{
if (this->_M_is_engaged())
- this->_M_get() = __u._M_get();
+ this->_M_get() = __u._M_fwd();
else
- this->_M_construct(__u._M_get());
+ this->_M_construct(__u._M_fwd());
}
else
{
if (__u)
{
if (this->_M_is_engaged())
- this->_M_get() = std::move(__u._M_get());
+ this->_M_get() = std::move(__u)._M_fwd();
else
- this->_M_construct(std::move(__u._M_get()));
+ this->_M_construct(std::move(__u)._M_fwd());
}
else
{
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())
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())
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())
private:
using _Base::_M_get;
+ [[__gnu__::__always_inline__]]
+ constexpr _Tp&
+ _M_fwd() & noexcept
+ { return _M_get(); }
+
+ [[__gnu__::__always_inline__]]
+ constexpr _Tp&&
+ _M_fwd() && noexcept
+ { return std::move(_M_get()); }
+
+ [[__gnu__::__always_inline__]]
+ constexpr const _Tp&
+ _M_fwd() const& noexcept
+ { return _M_get(); }
+
+ [[__gnu__::__always_inline__]]
+ constexpr const _Tp&&
+ _M_fwd() const&& noexcept
+ { return std::move(_M_get()); }
+
template<typename _Up> friend class optional;
#if __cpp_lib_optional >= 202110L // C++23
#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._M_fwd());
+ }
+
+ 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._M_fwd());
+ }
+
+ 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)._M_fwd());
+ }
+
+ 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)._M_fwd());
+ }
+
+ 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;
+
+ [[__gnu__::__always_inline__]]
+ constexpr _Tp&
+ _M_fwd() const noexcept
+ { return *_M_val; }
+
+ 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>;
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
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
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.
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>>
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 );
--- /dev/null
+// { 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" }
--- /dev/null
+// { 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" }
--- /dev/null
+// { 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());
+}
--- /dev/null
+// { 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());
+}
--- /dev/null
+// { 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());
+}
--- /dev/null
+// { 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&>> );
--- /dev/null
+// { 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());
+}
--- /dev/null
+// { 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" }
+
--- /dev/null
+// { 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" }
--- /dev/null
+// { 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" }
--- /dev/null
+// { 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());
+}
--- /dev/null
+// { 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());
+}
# 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;
};
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;
};
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;
};
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;
};
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;
};
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> );
# 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>
#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; // { dg-error "here" }
+std::optional<const int &> o9; // { dg-error "here" "optional<T&> is a C++26 feature" { target c++23_down } }
+std::optional<std::in_place_t &> o10; // { dg-error "here" }
+std::optional<const std::in_place_t &> o11; // { dg-error "here" }
+std::optional<std::nullopt_t &> o12; // { dg-error "here" }
+std::optional<const std::nullopt_t &> o13; // { dg-error "here" }
+std::optional<int &&> o14; // { dg-error "here" }
+std::optional<const int &&> o15; // { dg-error "here" }
// { dg-error "static assertion failed" "" { target *-*-* } 0 }
# error "Feature test macro for optional has wrong value for C++17 in <version>"
#elif __cplusplus == 202002L && __cpp_lib_optional != 202106L
# error "Feature test macro for optional has wrong value for C++20 in <version>"
-#elif __cplusplus > 202002L && __cpp_lib_optional != 202110L
+#elif __cplusplus == 202302L && __cpp_lib_optional != 202110L
# error "Feature test macro for optional has wrong value for C++23 in <version>"
+#elif __cplusplus > 202302L && __cpp_lib_optional != 202506L
+# error "Feature test macro for optional has wrong value for C++26 in <version>"
#endif
#if __cplusplus >= 202302L