@@ -106,6 +106,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
return *this;
}
+#if __cplusplus > 202002L
+ constexpr const _Bit_reference&
+ operator=(bool __x) const noexcept
+ {
+ if (__x)
+ *_M_p |= _M_mask;
+ else
+ *_M_p &= ~_M_mask;
+ return *this;
+ }
+#endif
+
_GLIBCXX20_CONSTEXPR
_Bit_reference&
operator=(const _Bit_reference& __x) _GLIBCXX_NOEXCEPT
@@ -212,6 +212,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
swap(second, __p.second);
}
+#if __cplusplus > 202002L
+ /// Swap the first members and then the second members.
+ constexpr void
+ swap(const pair& __p) const
+ noexcept(__and_<__is_nothrow_swappable<const _T1>,
+ __is_nothrow_swappable<const _T2>>::value)
+ {
+ using std::swap;
+ swap(first, __p.first);
+ swap(second, __p.second);
+ }
+#endif // C++23
+
private:
template<typename... _Args1, size_t... _Indexes1,
typename... _Args2, size_t... _Indexes2>
@@ -283,7 +296,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y))
{ }
- /// Converting constructor from a `pair<U1, U2>` lvalue
+ /// Converting constructor from a const `pair<U1, U2>` lvalue
template<typename _U1, typename _U2>
requires (_S_constructible<const _U1&, const _U2&>())
constexpr explicit(!_S_convertible<const _U1&, const _U2&>())
@@ -292,7 +305,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: first(__p.first), second(__p.second)
{ }
- /// Converting constructor from a `pair<U1, U2>` rvalue
+ /// Converting constructor from a non-const `pair<U1, U2>` rvalue
template<typename _U1, typename _U2>
requires (_S_constructible<_U1, _U2>())
constexpr explicit(!_S_convertible<_U1, _U2>())
@@ -302,6 +315,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
second(std::forward<_U2>(__p.second))
{ }
+#if __cplusplus > 202002L
+ /// Converting constructor from a non-const `pair<U1, U2>` lvalue
+ template<typename _U1, typename _U2>
+ requires (_S_constructible<_U1&, _U2&>())
+ constexpr explicit(!_S_convertible<_U1&, _U2&>())
+ pair(pair<_U1, _U2>& __p)
+ noexcept(_S_nothrow_constructible<_U1&, _U2&>())
+ : first(__p.first), second(__p.second)
+ { }
+
+ /// Converting constructor from a const `pair<U1, U2>` rvalue
+ template<typename _U1, typename _U2>
+ requires (_S_constructible<const _U1, const _U2>())
+ constexpr explicit(!_S_convertible<const _U1, const _U2>())
+ pair(const pair<_U1, _U2>&& __p)
+ noexcept(_S_nothrow_constructible<const _U1, const _U2>())
+ : first(std::forward<const _U1>(__p.first)),
+ second(std::forward<const _U2>(__p.second))
+ { }
+#endif
+
private:
/// @cond undocumented
template<typename _U1, typename _U2>
@@ -349,7 +383,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return *this;
}
- /// Converting assignment from a `pair<U1, U2>` lvalue
+ /// Converting assignment from a const `pair<U1, U2>` lvalue
template<typename _U1, typename _U2>
constexpr pair&
operator=(const pair<_U1, _U2>& __p)
@@ -361,7 +395,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return *this;
}
- /// Converting assignment from a `pair<U1, U2>` rvalue
+ /// Converting assignment from a non-const `pair<U1, U2>` rvalue
template<typename _U1, typename _U2>
constexpr pair&
operator=(pair<_U1, _U2>&& __p)
@@ -372,7 +406,55 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
second = std::forward<_U2>(__p.second);
return *this;
}
-#else
+
+#if __cplusplus > 202002L
+ /// Copy assignment operator
+ constexpr const pair&
+ operator=(const pair& __p) const
+ requires is_copy_assignable_v<const first_type>
+ && is_copy_assignable_v<const second_type>
+ {
+ first = __p.first;
+ second = __p.second;
+ return *this;
+ }
+
+ /// Move assignment operator
+ constexpr const pair&
+ operator=(pair&& __p) const
+ requires is_assignable_v<const first_type&, first_type>
+ && is_assignable_v<const second_type&, second_type>
+ {
+ first = std::forward<first_type>(__p.first);
+ second = std::forward<second_type>(__p.second);
+ return *this;
+ }
+
+ /// Converting assignment from a const `pair<U1, U2>` lvalue
+ template<typename _U1, typename _U2>
+ constexpr const pair&
+ operator=(const pair<_U1, _U2>& __p) const
+ requires is_assignable_v<const first_type&, const _U1&>
+ && is_assignable_v<const second_type&, const _U2&>
+ {
+ first = __p.first;
+ second = __p.second;
+ return *this;
+ }
+
+ /// Converting assignment from a non-const `pair<U1, U2>` rvalue
+ template<typename _U1, typename _U2>
+ constexpr const pair&
+ operator=(pair<_U1, _U2>&& __p) const
+ requires is_assignable_v<const first_type&, _U1>
+ && is_assignable_v<const second_type&, _U2>
+ {
+ first = std::forward<_U1>(__p.first);
+ second = std::forward<_U2>(__p.second);
+ return *this;
+ }
+#endif // C++23
+#else // !__cpp_lib_concepts
// C++11/14/17 implementation using enable_if, partially constexpr.
/** The default constructor creates @c first and @c second using their
@@ -710,6 +792,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
noexcept(noexcept(__x.swap(__y)))
{ __x.swap(__y); }
+#if __cplusplus > 202002L
+ template<typename _T1, typename _T2>
+ requires is_swappable<const _T1>::value && is_swappable<const _T2>::value
+ constexpr void
+ swap(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
+ noexcept(noexcept(__x.swap(__y)))
+ { __x.swap(__y); }
+#endif // C++23
+
#if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11
template<typename _T1, typename _T2>
typename enable_if<!__and_<__is_swappable<_T1>,
@@ -918,6 +1009,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
get(const pair<_Up, _Tp>&& __p) noexcept
{ return std::move(__p.second); }
+#if __cplusplus > 202002L
+ template<typename _T1, typename _T2, typename _U1, typename _U2,
+ template<typename> class _TQual, template<typename> class _UQual>
+ requires requires { typename pair<common_reference_t<_TQual<_T1>, _UQual<_U1>>,
+ common_reference_t<_TQual<_T2>, _UQual<_U2>>>; }
+ struct basic_common_reference<pair<_T1, _T2>, pair<_U1, _U2>, _TQual, _UQual>
+ {
+ using type = pair<common_reference_t<_TQual<_T1>, _UQual<_U1>>,
+ common_reference_t<_TQual<_T2>, _UQual<_U2>>>;
+ };
+
+ template<typename _T1, typename _T2, typename _U1, typename _U2>
+ requires requires { typename pair<common_type_t<_T1, _U1>, common_type_t<_T2, _U2>>; }
+ struct common_type<pair<_T1, _T2>, pair<_U1, _U2>>
+ { using type = pair<common_type_t<_T1, _U1>, common_type_t<_T2, _U2>>; };
+#endif
+
#endif // C++14
/// @}
#endif // C++11
@@ -107,6 +107,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
constexpr auto
uses_allocator_construction_args(const _Alloc&, pair<_Up, _Vp>&&) noexcept;
+#if __cplusplus > 202002L
+ template<_Std_pair _Tp, typename _Alloc, typename _Up, typename _Vp>
+ constexpr auto
+ uses_allocator_construction_args(const _Alloc&,
+ pair<_Up, _Vp>&) noexcept;
+
+ template<_Std_pair _Tp, typename _Alloc, typename _Up, typename _Vp>
+ constexpr auto
+ uses_allocator_construction_args(const _Alloc&, const pair<_Up, _Vp>&&) noexcept;
+#endif
+
template<_Std_pair _Tp, typename _Alloc, typename _Tuple1, typename _Tuple2>
constexpr auto
uses_allocator_construction_args(const _Alloc& __a, piecewise_construct_t,
@@ -181,6 +192,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
std::move(__pr).second));
}
+#if __cplusplus > 202002L
+ template<_Std_pair _Tp, typename _Alloc, typename _Up, typename _Vp>
+ constexpr auto
+ uses_allocator_construction_args(const _Alloc& __a,
+ pair<_Up, _Vp>& __pr) noexcept
+ {
+ using _Tp1 = typename _Tp::first_type;
+ using _Tp2 = typename _Tp::second_type;
+
+ return std::make_tuple(piecewise_construct,
+ std::uses_allocator_construction_args<_Tp1>(__a, __pr.first),
+ std::uses_allocator_construction_args<_Tp2>(__a, __pr.second));
+ }
+
+ template<_Std_pair _Tp, typename _Alloc, typename _Up, typename _Vp>
+ constexpr auto
+ uses_allocator_construction_args(const _Alloc& __a,
+ const pair<_Up, _Vp>&& __pr) noexcept
+ {
+ using _Tp1 = typename _Tp::first_type;
+ using _Tp2 = typename _Tp::second_type;
+
+ return std::make_tuple(piecewise_construct,
+ std::uses_allocator_construction_args<_Tp1>(__a,
+ std::move(__pr).first),
+ std::uses_allocator_construction_args<_Tp2>(__a,
+ std::move(__pr).second));
+ }
+#endif
+
template<typename _Tp, typename _Alloc, typename... _Args>
constexpr _Tp
make_obj_using_allocator(const _Alloc& __a, _Args&&... __args)
@@ -316,6 +316,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in)))
{ }
+#if __cplusplus > 202002L
+ template<typename... _UElements>
+ constexpr
+ _Tuple_impl(_Tuple_impl<_Idx, _UElements...>& __in)
+ : _Inherited(_Tuple_impl<_Idx, _UElements...>::_M_tail(__in)),
+ _Base(_Tuple_impl<_Idx, _UElements...>::_M_head(__in))
+ { }
+
+ template<typename _UHead, typename... _UTails>
+ constexpr
+ _Tuple_impl(const _Tuple_impl<_Idx, _UHead, _UTails...>&& __in)
+ : _Inherited(std::move
+ (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in))),
+ _Base(std::forward<const _UHead>
+ (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in)))
+ { }
+#endif
+
template<typename _Alloc>
_GLIBCXX20_CONSTEXPR
_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a)
@@ -379,6 +397,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in)))
{ }
+#if __cplusplus > 202002L
+ template<typename _Alloc, typename _UHead, typename... _UTails>
+ constexpr
+ _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+ _Tuple_impl<_Idx, _UHead, _UTails...>& __in)
+ : _Inherited(__tag, __a,
+ _Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in)),
+ _Base(__use_alloc<_Head, _Alloc, _UHead&>(__a),
+ _Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in))
+ { }
+
+ template<typename _Alloc, typename _UHead, typename... _UTails>
+ constexpr
+ _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+ const _Tuple_impl<_Idx, _UHead, _UTails...>&& __in)
+ : _Inherited(__tag, __a, std::move
+ (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in))),
+ _Base(__use_alloc<_Head, _Alloc, const _UHead>(__a),
+ std::forward<const _UHead>
+ (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in)))
+ { }
+#endif
+
template<typename... _UElements>
_GLIBCXX20_CONSTEXPR
void
@@ -400,6 +441,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
std::move(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in)));
}
+#if __cplusplus > 202002L
+ template<typename... _UElements>
+ constexpr void
+ _M_assign(const _Tuple_impl<_Idx, _UElements...>& __in) const
+ {
+ _M_head(*this) = _Tuple_impl<_Idx, _UElements...>::_M_head(__in);
+ _M_tail(*this)._M_assign(
+ _Tuple_impl<_Idx, _UElements...>::_M_tail(__in));
+ }
+
+ template<typename _UHead, typename... _UTails>
+ constexpr void
+ _M_assign(_Tuple_impl<_Idx, _UHead, _UTails...>&& __in) const
+ {
+ _M_head(*this) = std::forward<_UHead>
+ (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in));
+ _M_tail(*this)._M_assign(
+ std::move(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in)));
+ }
+#endif
+
protected:
_GLIBCXX20_CONSTEXPR
void
@@ -409,6 +471,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
swap(_M_head(*this), _M_head(__in));
_Inherited::_M_swap(_M_tail(__in));
}
+
+#if __cplusplus > 202002L
+ constexpr void
+ _M_swap(const _Tuple_impl& __in) const
+ {
+ using std::swap;
+ swap(_M_head(*this), _M_head(__in));
+ _Inherited::_M_swap(_M_tail(__in));
+ }
+#endif
};
// Basis case of inheritance recursion.
@@ -469,6 +541,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _Base(std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
{ }
+#if __cplusplus > 202002L
+ template<typename _UHead>
+ constexpr
+ _Tuple_impl(_Tuple_impl<_Idx, _UHead>& __in)
+ : _Base(_Tuple_impl<_Idx, _UHead>::_M_head(__in))
+ { }
+
+ template<typename _UHead>
+ constexpr
+ _Tuple_impl(const _Tuple_impl<_Idx, _UHead>&& __in)
+ : _Base(std::forward<const _UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
+ { }
+#endif
+
template<typename _Alloc>
_GLIBCXX20_CONSTEXPR
_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a)
@@ -521,6 +607,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
{ }
+#if __cplusplus > 202002L
+ template<typename _Alloc, typename _UHead>
+ constexpr
+ _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+ _Tuple_impl<_Idx, _UHead>& __in)
+ : _Base(__use_alloc<_Head, _Alloc, _UHead&>(__a),
+ _Tuple_impl<_Idx, _UHead>::_M_head(__in))
+ { }
+
+ template<typename _Alloc, typename _UHead>
+ constexpr
+ _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+ const _Tuple_impl<_Idx, _UHead>&& __in)
+ : _Base(__use_alloc<_Head, _Alloc, const _UHead>(__a),
+ std::forward<const _UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
+ { }
+#endif
+
template<typename _UHead>
_GLIBCXX20_CONSTEXPR
void
@@ -538,6 +642,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
= std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in));
}
+#if __cplusplus > 202002L
+ template<typename _UHead>
+ constexpr void
+ _M_assign(const _Tuple_impl<_Idx, _UHead>& __in) const
+ {
+ _M_head(*this) = _Tuple_impl<_Idx, _UHead>::_M_head(__in);
+ }
+
+ template<typename _UHead>
+ constexpr void
+ _M_assign(_Tuple_impl<_Idx, _UHead>&& __in) const
+ {
+ _M_head(*this)
+ = std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in));
+ }
+#endif
+
protected:
_GLIBCXX20_CONSTEXPR
void
@@ -546,6 +667,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
using std::swap;
swap(_M_head(*this), _M_head(__in));
}
+
+#if __cplusplus > 202002L
+ constexpr void
+ _M_swap(const _Tuple_impl& __in) const
+ {
+ using std::swap;
+ swap(_M_head(*this), _M_head(__in));
+ }
+#endif
};
// Concept utility functions, reused in conditionally-explicit
@@ -728,6 +858,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static constexpr bool __use_other_ctor()
{ return _UseOtherCtor<_Tuple>::value; }
+#if __cplusplus > 202002L
+ template<typename... _Args>
+ static constexpr bool __constructible
+ = _TCC<true>::template __constructible<_Args...>;
+
+ template<typename... _Args>
+ static constexpr bool __convertible
+ = _TCC<true>::template __convertible<_Args...>;
+#endif
+
public:
template<typename _Dummy = void,
_ImplicitDefaultCtor<is_void<_Dummy>::value> = true>
@@ -815,6 +955,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
noexcept(__nothrow_constructible<_UElements...>())
: _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) { }
+#if __cplusplus > 202002L
+ template<typename... _UElements>
+ requires (sizeof...(_Elements) == sizeof...(_UElements))
+ && (!__use_other_ctor<tuple<_UElements...>&>())
+ && __constructible<_UElements&...>
+ explicit(!__convertible<_UElements&...>)
+ constexpr
+ tuple(tuple<_UElements...>& __in)
+ noexcept(__nothrow_constructible<_UElements&...>())
+ : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&>(__in))
+ { }
+
+ template<typename... _UElements>
+ requires (sizeof...(_Elements) == sizeof...(_UElements))
+ && (!__use_other_ctor<const tuple<_UElements...>&&>())
+ && __constructible<const _UElements...>
+ explicit(!__convertible<const _UElements...>)
+ constexpr
+ tuple(const tuple<_UElements...>&& __in)
+ noexcept(__nothrow_constructible<const _UElements...>())
+ : _Inherited(static_cast<const _Tuple_impl<0, _UElements...>&&>(__in)) { }
+#endif
+
// Allocator-extended constructors.
template<typename _Alloc,
@@ -913,6 +1076,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static_cast<_Tuple_impl<0, _UElements...>&&>(__in))
{ }
+#if __cplusplus > 202002L
+ template<typename _Alloc, typename... _UElements>
+ requires (sizeof...(_Elements) == sizeof...(_UElements))
+ && (!__use_other_ctor<tuple<_UElements...>&>())
+ && __constructible<_UElements&...>
+ explicit(!__convertible<_UElements&...>)
+ constexpr
+ tuple(allocator_arg_t __tag, const _Alloc& __a,
+ tuple<_UElements...>& __in)
+ : _Inherited(__tag, __a,
+ static_cast<_Tuple_impl<0, _UElements...>&>(__in))
+ { }
+
+ template<typename _Alloc, typename... _UElements>
+ requires (sizeof...(_Elements) == sizeof...(_UElements))
+ && (!__use_other_ctor<const tuple<_UElements...>>())
+ && __constructible<const _UElements...>
+ explicit(!__convertible<const _UElements...>)
+ constexpr
+ tuple(allocator_arg_t __tag, const _Alloc& __a,
+ const tuple<_UElements...>&& __in)
+ : _Inherited(__tag, __a,
+ static_cast<const _Tuple_impl<0, _UElements...>&&>(__in))
+ { }
+#endif
+
// tuple assignment
_GLIBCXX20_CONSTEXPR
@@ -957,12 +1146,57 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return *this;
}
+#if __cplusplus > 202002L
+ constexpr const tuple&
+ operator=(const tuple& __in) const
+ requires (is_copy_assignable_v<const _Elements> && ...)
+ {
+ this->_M_assign(__in);
+ return *this;
+ }
+
+ constexpr const tuple&
+ operator=(tuple&& __in) const
+ requires (is_assignable_v<const _Elements&, _Elements> && ...)
+ {
+ this->_M_assign(std::move(__in));
+ return *this;
+ }
+
+ template<typename... _UElements>
+ constexpr const tuple&
+ operator=(const tuple<_UElements...>& __in) const
+ requires (sizeof...(_Elements) == sizeof...(_UElements))
+ && (is_assignable_v<const _Elements&, const _UElements&> && ...)
+ {
+ this->_M_assign(__in);
+ return *this;
+ }
+
+ template<typename... _UElements>
+ constexpr const tuple&
+ operator=(tuple<_UElements...>&& __in) const
+ requires (sizeof...(_Elements) == sizeof...(_UElements))
+ && (is_assignable_v<const _Elements&, _UElements> && ...)
+ {
+ this->_M_assign(std::move(__in));
+ return *this;
+ }
+#endif
+
// tuple swap
_GLIBCXX20_CONSTEXPR
void
swap(tuple& __in)
noexcept(__and_<__is_nothrow_swappable<_Elements>...>::value)
{ _Inherited::_M_swap(__in); }
+
+#if __cplusplus > 202002L
+ constexpr void
+ swap(const tuple& __in) const
+ noexcept(__and_<__is_nothrow_swappable<const _Elements>...>::value)
+ { _Inherited::_M_swap(__in); }
+#endif
};
#if __cpp_deduction_guides >= 201606
@@ -985,6 +1219,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
public:
_GLIBCXX20_CONSTEXPR
void swap(tuple&) noexcept { /* no-op */ }
+#if __cplusplus > 202002L
+ constexpr void swap(const tuple&) const noexcept { /* no-op */ }
+#endif
// We need the default since we're going to define no-op
// allocator constructors.
tuple() = default;
@@ -1064,6 +1301,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static constexpr bool __is_alloc_arg()
{ return is_same<__remove_cvref_t<_U1>, allocator_arg_t>::value; }
+#if __cplusplus > 202002L
+ template<typename _U1, typename _U2>
+ static constexpr bool __constructible
+ = _TCC<true>::template __constructible<_U1, _U2>;
+
+ template<typename _U1, typename _U2>
+ static constexpr bool __convertible
+ = _TCC<true>::template __convertible<_U1, _U2>;
+#endif
+
public:
template<bool _Dummy = true,
_ImplicitDefaultCtor<_Dummy, _T1, _T2> = true>
@@ -1139,6 +1386,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
noexcept(__nothrow_constructible<_U1, _U2>())
: _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) { }
+#if __cplusplus > 202002L
+ template<typename _U1, typename _U2>
+ requires __constructible<_U1&, _U2&>
+ explicit(!__convertible<_U1&, _U2&>)
+ constexpr
+ tuple(tuple<_U1, _U2>& __in)
+ noexcept(__nothrow_constructible<_U1&, _U2&>())
+ : _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&>(__in)) { }
+
+ template<typename _U1, typename _U2>
+ requires __constructible<const _U1, const _U2>
+ explicit(!__convertible<const _U1, const _U2>)
+ constexpr
+ tuple(const tuple<_U1, _U2>&& __in)
+ noexcept(__nothrow_constructible<const _U1, const _U2>())
+ : _Inherited(static_cast<const _Tuple_impl<0, _U1, _U2>&&>(__in)) { }
+#endif
+
template<typename _U1, typename _U2,
_ImplicitCtor<true, const _U1&, const _U2&> = true>
constexpr
@@ -1169,6 +1434,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _Inherited(std::forward<_U1>(__in.first),
std::forward<_U2>(__in.second)) { }
+#if __cplusplus > 202002L
+ template<typename _U1, typename _U2>
+ requires __constructible<_U1&, _U2&>
+ explicit(!__convertible<_U1&, _U2&>)
+ constexpr
+ tuple(pair<_U1, _U2>& __in)
+ noexcept(__nothrow_constructible<_U1&, _U2&>())
+ : _Inherited(__in.first, __in.second) { }
+
+ template<typename _U1, typename _U2>
+ requires __constructible<const _U1, const _U2>
+ explicit(!__convertible<const _U1, const _U2>)
+ constexpr
+ tuple(const pair<_U1, _U2>&& __in)
+ noexcept(__nothrow_constructible<const _U1, const _U2>())
+ : _Inherited(std::forward<const _U1>(__in.first),
+ std::forward<const _U2>(__in.second)) { }
+#endif
+
// Allocator-extended constructors.
template<typename _Alloc,
@@ -1252,6 +1536,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _Inherited(__tag, __a, static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in))
{ }
+#if __cplusplus > 202002L
+ template<typename _Alloc, typename _U1, typename _U2>
+ requires __constructible<_U1&, _U2&>
+ explicit(!__convertible<_U1&, _U2&>)
+ constexpr
+ tuple(allocator_arg_t __tag, const _Alloc& __a,
+ tuple<_U1, _U2>& __in)
+ : _Inherited(__tag, __a,
+ static_cast<_Tuple_impl<0, _U1, _U2>&>(__in))
+ { }
+
+ template<typename _Alloc, typename _U1, typename _U2>
+ requires __constructible<const _U1, const _U2>
+ explicit(!__convertible<const _U1, const _U2>)
+ constexpr
+ tuple(allocator_arg_t __tag, const _Alloc& __a,
+ const tuple<_U1, _U2>&& __in)
+ : _Inherited(__tag, __a,
+ static_cast<const _Tuple_impl<0, _U1, _U2>&&>(__in))
+ { }
+#endif
+
template<typename _Alloc, typename _U1, typename _U2,
_ImplicitCtor<true, const _U1&, const _U2&> = true>
_GLIBCXX20_CONSTEXPR
@@ -1282,6 +1588,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _Inherited(__tag, __a, std::forward<_U1>(__in.first),
std::forward<_U2>(__in.second)) { }
+#if __cplusplus > 202002L
+ template<typename _Alloc, typename _U1, typename _U2>
+ requires __constructible<_U1&, _U2&>
+ explicit(!__convertible<_U1&, _U2&>)
+ constexpr
+ tuple(allocator_arg_t __tag, const _Alloc& __a,
+ pair<_U1, _U2>& __in)
+ : _Inherited(__tag, __a, __in.first, __in.second) { }
+
+ template<typename _Alloc, typename _U1, typename _U2>
+ requires __constructible<const _U1, const _U2>
+ explicit(!__convertible<const _U1, const _U2>)
+ constexpr
+ tuple(allocator_arg_t __tag, const _Alloc& __a, const pair<_U1, _U2>&& __in)
+ : _Inherited(__tag, __a, std::forward<const _U1>(__in.first),
+ std::forward<const _U2>(__in.second)) { }
+#endif
+
// Tuple assignment.
_GLIBCXX20_CONSTEXPR
@@ -1326,6 +1650,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return *this;
}
+#if __cplusplus > 202002L
+ constexpr const tuple&
+ operator=(const tuple& __in) const
+ requires is_copy_assignable_v<const _T1> && is_copy_assignable_v<const _T2>
+ {
+ this->_M_assign(__in);
+ return *this;
+ }
+
+ constexpr const tuple&
+ operator=(tuple&& __in) const
+ requires is_assignable_v<const _T1&, _T1> && is_assignable_v<const _T2, _T2>
+ {
+ this->_M_assign(std::move(__in));
+ return *this;
+ }
+
+ template<typename _U1, typename _U2>
+ constexpr const tuple&
+ operator=(const tuple<_U1, _U2>& __in) const
+ requires is_assignable_v<const _T1&, const _U1&>
+ && is_assignable_v<const _T2&, const _U2&>
+ {
+ this->_M_assign(__in);
+ return *this;
+ }
+
+ template<typename _U1, typename _U2>
+ constexpr const tuple&
+ operator=(tuple<_U1, _U2>&& __in) const
+ requires is_assignable_v<const _T1&, _U1>
+ && is_assignable_v<const _T2&, _U2>
+ {
+ this->_M_assign(std::move(__in));
+ return *this;
+ }
+#endif
+
template<typename _U1, typename _U2>
_GLIBCXX20_CONSTEXPR
__enable_if_t<__assignable<const _U1&, const _U2&>(), tuple&>
@@ -1348,12 +1710,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return *this;
}
+#if __cplusplus > 202002L
+ template<typename _U1, typename _U2>
+ constexpr const tuple&
+ operator=(const pair<_U1, _U2>& __in) const
+ requires is_assignable_v<const _T1&, const _U1&>
+ && is_assignable_v<const _T2&, const _U2&>
+ {
+ this->_M_head(*this) = __in.first;
+ this->_M_tail(*this)._M_head(*this) = __in.second;
+ return *this;
+ }
+
+ template<typename _U1, typename _U2>
+ constexpr const tuple&
+ operator=(pair<_U1, _U2>&& __in) const
+ requires is_assignable_v<const _T1&, _U1>
+ && is_assignable_v<const _T2&, _U2>
+ {
+ this->_M_head(*this) = std::forward<_U1>(__in.first);
+ this->_M_tail(*this)._M_head(*this) = std::forward<_U2>(__in.second);
+ return *this;
+ }
+#endif
+
_GLIBCXX20_CONSTEXPR
void
swap(tuple& __in)
noexcept(__and_<__is_nothrow_swappable<_T1>,
__is_nothrow_swappable<_T2>>::value)
{ _Inherited::_M_swap(__in); }
+
+#if __cplusplus > 202002L
+ constexpr void
+ swap(const tuple& __in) const
+ noexcept(__and_<__is_nothrow_swappable<const _T1>,
+ __is_nothrow_swappable<const _T2>>::value)
+ { _Inherited::_M_swap(__in); }
+#endif
};
@@ -1781,6 +2175,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
noexcept(noexcept(__x.swap(__y)))
{ __x.swap(__y); }
+#if __cplusplus > 202002L
+ template<typename... _Elements>
+ requires (__is_swappable<const _Elements>::value && ...)
+ constexpr void
+ swap(const tuple<_Elements...>& __x, const tuple<_Elements...>& __y)
+ noexcept(noexcept(__x.swap(__y)))
+ { __x.swap(__y); }
+#endif
+
#if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11
template<typename... _Elements>
_GLIBCXX20_CONSTEXPR
@@ -1905,6 +2308,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
#endif // C++17
+#if __cplusplus > 202002L
+ template<typename... _TTypes, typename... _UTypes,
+ template<typename> class TQual, template<typename> class UQual>
+ requires requires { typename tuple<common_reference_t<TQual<_TTypes>, UQual<_UTypes>>...>; }
+ struct basic_common_reference<tuple<_TTypes...>, tuple<_UTypes...>, TQual, UQual>
+ { using type = tuple<common_reference_t<TQual<_TTypes>, UQual<_UTypes>>...>; };
+
+ template<typename... _TTypes, typename... _UTypes>
+ requires requires { typename tuple<common_type_t<_TTypes, _UTypes>...>; }
+ struct common_type<tuple<_TTypes...>, tuple<_UTypes...>>
+ { using type = tuple<common_type_t<_TTypes, _UTypes>...>; };
+#endif
+
/// @}
_GLIBCXX_END_NAMESPACE_VERSION
new file mode 100644
@@ -0,0 +1,208 @@
+// Verify P2321R2 "zip" enhancements to std::pair.
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <utility>
+#include <testsuite_hooks.h>
+
+using std::pair;
+
+struct A { };
+
+constexpr bool
+test01()
+{
+ struct B { bool v; constexpr B(A&) : v(true) { } };
+
+ // template<class U1, class U2>
+ // constexpr explicit(false) pair(pair<U1, U2>&);
+
+ pair<A, int> p2a0;
+ pair<B, int> p2b0 = p2a0;
+ VERIFY( std::get<0>(p2b0).v );
+
+ pair<int, A> p2a1;
+ pair<int, B> p2b1 = p2a1;
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+constexpr bool
+test02()
+{
+ struct B { bool v; explicit constexpr B(A&) : v(true) { } };
+
+ // template<class U1, class U2>
+ // constexpr explicit(true) pair(pair<U1, U2>&);
+
+ static_assert(!std::is_convertible_v<pair<A, int>&, pair<B, int>>);
+ static_assert(!std::is_convertible_v<pair<int, A>&, pair<int, B>>);
+
+ pair<A, int> p2a0;
+ pair<B, int> p2b0(p2a0);
+ VERIFY( std::get<0>(p2b0).v );
+
+ pair<int, A> p2a1;
+ pair<int, B> p2b1(p2a1);
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+constexpr bool
+test03()
+{
+ struct B { bool v; constexpr B(const A&&) : v(true) { } };
+
+ // template<class U1, class U2>
+ // constexpr explicit(false) pair(const pair<U1, U2>&&);
+
+ const pair<A, int> p2a0;
+ pair<B, int> p2b0 = std::move(p2a0);
+ VERIFY( std::get<0>(p2b0).v );
+
+ const pair<int, A> p2a1;
+ pair<int, B> p2b1 = std::move(p2a1);
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+constexpr bool
+test04()
+{
+ struct B { bool v; explicit constexpr B(const A&&) : v(true) { } };
+
+ // template<class U1, class U2>
+ // constexpr explicit(true) pair(const pair<U1, U2>&&);
+
+ static_assert(!std::is_convertible_v<const pair<A, int>&&, pair<B, int>>);
+ static_assert(!std::is_convertible_v<const pair<int, A>&&, pair<int, B>>);
+
+ const pair<A, int> p2a0;
+ pair<B, int> p2b0(std::move(p2a0));
+ VERIFY( std::get<0>(p2b0).v );
+
+ const pair<int, A> p2a1;
+ pair<int, B> p2b1(std::move(p2a1));
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+constexpr bool
+test05()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(const A&) const { v = true; return *this; }
+ };
+
+ // template<class U1, class U2>
+ // constexpr const pair& operator=(const pair<U1, U2>&) const;
+
+ const pair<A, A> p2a;
+ const pair<B, B> p2b;
+ p2b = p2a;
+
+ return true;
+}
+
+constexpr bool
+test06()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(A&&) const { v = true; return *this; }
+ };
+
+ // template<class U1, class U2>
+ // constexpr const pair& operator=(pair<U1, U2>&&) const;
+
+ pair<A, A> p2a;
+ const pair<B, B> p2b;
+ p2b = std::move(p2a);
+
+ return true;
+}
+
+constexpr bool
+test07()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(const B&) const { v = true; return *this; }
+ };
+
+ // constexpr const pair& operator=(const pair&) const;
+
+ const pair<B, B> t2a;
+ const pair<B, B> t2b;
+ t2b = t2a;
+ VERIFY( std::get<0>(t2b).v );
+ VERIFY( std::get<1>(t2b).v );
+
+ return true;
+}
+
+constexpr bool
+test08()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(B&&) const { v = true; return *this; }
+ };
+
+ // constexpr const pair& operator=(pair&&) const;
+
+ pair<B, B> t2a;
+ const pair<B, B> t2b;
+ t2b = std::move(t2a);
+ VERIFY( std::get<0>(t2b).v );
+ VERIFY( std::get<1>(t2b).v );
+
+ return true;
+}
+
+struct S
+{
+ mutable int v = 0;
+ friend constexpr void swap(S&& x, S&& y) = delete;
+ friend constexpr void swap(const S& x, const S& y) { ++x.v; ++y.v; }
+};
+
+constexpr bool
+test09()
+{
+ const pair<S, S> t2, u2;
+ std::swap(t2, u2);
+ VERIFY( std::get<0>(t2).v == 1 );
+ VERIFY( std::get<0>(u2).v == 1 );
+ VERIFY( std::get<1>(t2).v == 1 );
+ VERIFY( std::get<1>(u2).v == 1 );
+
+ static_assert(!std::is_swappable_v<const pair<A, int>&>);
+ static_assert(!std::is_swappable_v<const pair<int, A>&>);
+
+ return true;
+}
+
+int
+main()
+{
+ static_assert(test01());
+ static_assert(test02());
+ static_assert(test03());
+ static_assert(test04());
+ // FIXME: G++ doesn't support reading mutable members during constexpr (PR c++/92505).
+ test05();
+ test06();
+ test07();
+ test08();
+ test09();
+}
new file mode 100644
@@ -0,0 +1,664 @@
+// Verify P2321R2 "zip" enhancements to std::tuple.
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <tuple>
+#include <memory>
+#include <testsuite_hooks.h>
+
+using std::tuple;
+using std::pair;
+using std::allocator;
+using std::allocator_arg_t;
+using std::allocator_arg;
+
+namespace alloc {
+ struct B01;
+ struct B02;
+ struct B03;
+ struct B04;
+}
+
+template<> struct std::uses_allocator<alloc::B01, allocator<int>> : std::true_type { };
+template<> struct std::uses_allocator<alloc::B02, allocator<int>> : std::true_type { };
+template<> struct std::uses_allocator<alloc::B03, allocator<int>> : std::true_type { };
+template<> struct std::uses_allocator<alloc::B04, allocator<int>> : std::true_type { };
+
+struct A { };
+
+constexpr bool
+test01()
+{
+ struct B { bool v; constexpr B(A&) : v(true) { } };
+
+ // template<class... UTypes>
+ // constexpr explicit(false) tuple(tuple<UTypes...>&);
+
+ tuple<A> t1a;
+ tuple<B> t1b = t1a;
+ VERIFY( std::get<0>(t1b).v );
+
+ tuple<A, int> t2a0;
+ tuple<B, int> t2b0 = t2a0;
+ VERIFY( std::get<0>(t2b0).v );
+
+ tuple<int, A> t2a1;
+ tuple<int, B> t2b1 = t2a1;
+ VERIFY( std::get<1>(t2b1).v );
+
+ tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0 = t3a0;
+ VERIFY( std::get<0>(t3b0).v );
+
+ tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1 = t3a1;
+ VERIFY( std::get<1>(t3b1).v );
+
+ tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2 = t3a2;
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class... UTypes>
+ // constexpr explicit(false) tuple(pair<UTypes...>&);
+
+ pair<A, int> p2a0;
+ tuple<B, int> p2b0 = p2a0;
+ VERIFY( std::get<0>(p2b0).v );
+
+ pair<int, A> p2a1;
+ tuple<int, B> p2b1 = p2a1;
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+namespace alloc
+{
+ struct B01
+ {
+ bool v;
+ B01(A&);
+ constexpr B01(allocator_arg_t, allocator<int>, A&) : v(true) { }
+ };
+
+ constexpr bool
+ test01()
+ {
+ using B = B01;
+
+ // template<class Alloc, class... UTypes>
+ // constexpr explicit(false)
+ // tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&);
+
+ tuple<A> t1a;
+ tuple<B> t1b = {allocator_arg, allocator<int>{}, t1a};
+ VERIFY( std::get<0>(t1b).v );
+
+ tuple<A, int> t2a0;
+ tuple<B, int> t2b0 = {allocator_arg, allocator<int>{}, t2a0};
+ VERIFY( std::get<0>(t2b0).v );
+
+ tuple<int, A> t2a1;
+ tuple<int, B> t2b1 = {allocator_arg, allocator<int>{}, t2a1};
+ VERIFY( std::get<1>(t2b1).v );
+
+ tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0 = {allocator_arg, allocator<int>{}, t3a0};
+ VERIFY( std::get<0>(t3b0).v );
+
+ tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1 = {allocator_arg, allocator<int>{}, t3a1};
+ VERIFY( std::get<1>(t3b1).v );
+
+ tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2 = {allocator_arg, allocator<int>{}, t3a2};
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class Alloc, class U1, class U2>
+ // constexpr explicit(false)
+ // tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&);
+
+ pair<A, int> p2a0;
+ tuple<B, int> p2b0 = {allocator_arg, allocator<int>{}, p2a0};
+ VERIFY( std::get<0>(p2b0).v );
+
+ pair<int, A> p2a1;
+ tuple<int, B> p2b1 = {allocator_arg, allocator<int>{}, p2a1};
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+ }
+}
+
+constexpr bool
+test02()
+{
+ struct B { bool v; explicit constexpr B(A&) : v(true) { } };
+
+ // template<class... UTypes>
+ // constexpr explicit(true) tuple(tuple<UTypes...>&);
+
+ static_assert(!std::is_convertible_v<tuple<A>&, tuple<B>>);
+
+ tuple<A> t1a;
+ tuple<B> t1b(t1a);
+ VERIFY( std::get<0>(t1b).v );
+
+ static_assert(!std::is_convertible_v<tuple<A, int>&, tuple<B, int>>);
+ static_assert(!std::is_convertible_v<tuple<int, A>&, tuple<int, B>>);
+
+ tuple<A, int> t2a0;
+ tuple<B, int> t2b0(t2a0);
+ VERIFY( std::get<0>(t2b0).v );
+
+ tuple<int, A> t2a1;
+ tuple<int, B> t2b1(t2a1);
+ VERIFY( std::get<1>(t2b1).v );
+
+ static_assert(!std::is_convertible_v<tuple<A, int, int>&, tuple<B, int, int>>);
+ static_assert(!std::is_convertible_v<tuple<int, A, int>&, tuple<int, B, int>>);
+ static_assert(!std::is_convertible_v<tuple<int, int, A>&, tuple<int, int, B>>);
+
+ tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0(t3a0);
+ VERIFY( std::get<0>(t3b0).v );
+
+ tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1(t3a1);
+ VERIFY( std::get<1>(t3b1).v );
+
+ tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2(t3a2);
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class... UTypes>
+ // constexpr explicit(true) tuple(pair<UTypes...>&);
+
+ static_assert(!std::is_convertible_v<pair<A, int>&, tuple<B, int>>);
+ static_assert(!std::is_convertible_v<pair<int, A>&, tuple<int, B>>);
+
+ pair<A, int> p2a0;
+ tuple<B, int> p2b0(p2a0);
+ VERIFY( std::get<0>(p2b0).v );
+
+ pair<int, A> p2a1;
+ tuple<int, B> p2b1(p2a1);
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+namespace alloc
+{
+ struct B02
+ {
+ bool v;
+ explicit B02(A&);
+ explicit constexpr B02(allocator_arg_t, allocator<int>, A&) : v(true) { }
+ };
+
+ constexpr bool
+ test02()
+ {
+ using B = B02;
+
+ // template<class Alloc, class... UTypes>
+ // constexpr explicit(true)
+ // tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&);
+
+ tuple<A> t1a;
+ tuple<B> t1b(allocator_arg, allocator<int>{}, t1a);
+ VERIFY( std::get<0>(t1b).v );
+
+ tuple<A, int> t2a0;
+ tuple<B, int> t2b0(allocator_arg, allocator<int>{}, t2a0);
+ VERIFY( std::get<0>(t2b0).v );
+
+ tuple<int, A> t2a1;
+ tuple<int, B> t2b1(allocator_arg, allocator<int>{}, t2a1);
+ VERIFY( std::get<1>(t2b1).v );
+
+ tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0(allocator_arg, allocator<int>{}, t3a0);
+ VERIFY( std::get<0>(t3b0).v );
+
+ tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1(allocator_arg, allocator<int>{}, t3a1);
+ VERIFY( std::get<1>(t3b1).v );
+
+ tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2(allocator_arg, allocator<int>{}, t3a2);
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class Alloc, class U1, class U2>
+ // constexpr explicit(true)
+ // tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&);
+
+ pair<A, int> p2a0;
+ tuple<B, int> p2b0(allocator_arg, allocator<int>{}, p2a0);
+ VERIFY( std::get<0>(p2b0).v );
+
+ pair<int, A> p2a1;
+ tuple<int, B> p2b1(allocator_arg, allocator<int>{}, p2a1);
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+ }
+} // namespace alloc
+
+constexpr bool
+test03()
+{
+ struct B { bool v; constexpr B(const A&&) : v(true) { } };
+
+ // template<class... UTypes>
+ // constexpr explicit(false) tuple(const tuple<UTypes...>&&);
+
+ const tuple<A> t1a;
+ tuple<B> t1b = std::move(t1a);
+ VERIFY( std::get<0>(t1b).v );
+
+ const tuple<A, int> t2a0;
+ tuple<B, int> t2b0 = std::move(t2a0);
+ VERIFY( std::get<0>(t2b0).v );
+
+ const tuple<int, A> t2a1;
+ tuple<int, B> t2b1 = std::move(t2a1);
+ VERIFY( std::get<1>(t2b1).v );
+
+ const tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0 = std::move(t3a0);
+ VERIFY( std::get<0>(t3b0).v );
+
+ const tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1 = std::move(t3a1);
+ VERIFY( std::get<1>(t3b1).v );
+
+ const tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2 = std::move(t3a2);
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class... UTypes>
+ // constexpr explicit(false) tuple(const pair<UTypes...>&&);
+
+ const pair<A, int> p2a0;
+ tuple<B, int> p2b0 = std::move(p2a0);
+ VERIFY( std::get<0>(p2b0).v );
+
+ const pair<int, A> p2a1;
+ tuple<int, B> p2b1 = std::move(p2a1);
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+namespace alloc
+{
+ struct B03
+ {
+ bool v;
+ B03(const A&&);
+ constexpr B03(allocator_arg_t, allocator<int>, const A&&) : v(true) { }
+ };
+
+ constexpr bool
+ test03()
+ {
+ using B = B03;
+
+ // template<class Alloc, class... UTypes>
+ // constexpr explicit(false)
+ // tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&&);
+
+ const tuple<A> t1a;
+ tuple<B> t1b = {allocator_arg, allocator<int>{}, std::move(t1a)};
+ VERIFY( std::get<0>(t1b).v );
+
+ const tuple<A, int> t2a0;
+ tuple<B, int> t2b0 = {allocator_arg, allocator<int>{}, std::move(t2a0)};
+ VERIFY( std::get<0>(t2b0).v );
+
+ const tuple<int, A> t2a1;
+ tuple<int, B> t2b1 = {allocator_arg, allocator<int>{}, std::move(t2a1)};
+ VERIFY( std::get<1>(t2b1).v );
+
+ const tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0 = {allocator_arg, allocator<int>{}, std::move(t3a0)};
+ VERIFY( std::get<0>(t3b0).v );
+
+ const tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1 = {allocator_arg, allocator<int>{}, std::move(t3a1)};
+ VERIFY( std::get<1>(t3b1).v );
+
+ const tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2 = {allocator_arg, allocator<int>{}, std::move(t3a2)};
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class Alloc, class U1, class U2>
+ // constexpr explicit(false)
+ // tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&&);
+
+ const pair<A, int> p2a0;
+ tuple<B, int> p2b0 = {allocator_arg, allocator<int>{}, std::move(p2a0)};
+ VERIFY( std::get<0>(p2b0).v );
+
+ const pair<int, A> p2a1;
+ tuple<int, B> p2b1 = {allocator_arg, allocator<int>{}, std::move(p2a1)};
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+ }
+};
+
+constexpr bool
+test04()
+{
+ struct B { bool v; explicit constexpr B(const A&&) : v(true) { } };
+
+ // template<class... UTypes>
+ // constexpr explicit(true) tuple(const tuple<UTypes...>&&);
+
+ static_assert(!std::is_convertible_v<tuple<A>&, tuple<B>>);
+
+ const tuple<A> t1a;
+ tuple<B> t1b(std::move(t1a));
+ VERIFY( std::get<0>(t1b).v );
+
+ static_assert(!std::is_convertible_v<tuple<A, int>&, tuple<B, int>>);
+ static_assert(!std::is_convertible_v<tuple<int, A>&, tuple<int, B>>);
+
+ const tuple<A, int> t2a0;
+ tuple<B, int> t2b0(std::move(t2a0));
+ VERIFY( std::get<0>(t2b0).v );
+
+ const tuple<int, A> t2a1;
+ tuple<int, B> t2b1(std::move(t2a1));
+ VERIFY( std::get<1>(t2b1).v );
+
+ static_assert(!std::is_convertible_v<tuple<A, int, int>&, tuple<B, int, int>>);
+ static_assert(!std::is_convertible_v<tuple<int, A, int>&, tuple<int, B, int>>);
+ static_assert(!std::is_convertible_v<tuple<int, int, A>&, tuple<int, int, B>>);
+
+ const tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0(std::move(t3a0));
+ VERIFY( std::get<0>(t3b0).v );
+
+ const tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1(std::move(t3a1));
+ VERIFY( std::get<1>(t3b1).v );
+
+ const tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2(std::move(t3a2));
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class... UTypes>
+ // constexpr explicit(true) tuple(const pair<UTypes...>&&);
+
+ static_assert(!std::is_convertible_v<pair<A, int>&, tuple<B, int>>);
+ static_assert(!std::is_convertible_v<pair<int, A>&, tuple<int, B>>);
+
+ const pair<A, int> p2a0;
+ tuple<B, int> p2b0(std::move(p2a0));
+ VERIFY( std::get<0>(p2b0).v );
+
+ const pair<int, A> p2a1;
+ tuple<int, B> p2b1(std::move(p2a1));
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+namespace alloc
+{
+ struct B04
+ {
+ bool v;
+ explicit B04(const A&&);
+ explicit constexpr B04(allocator_arg_t, allocator<int>, const A&&) : v(true) { }
+ };
+
+ constexpr bool
+ test04()
+ {
+ using B = B04;
+
+ // template<class Alloc, class... UTypes>
+ // constexpr explicit(true)
+ // tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&&);
+
+ const tuple<A> t1a;
+ tuple<B> t1b(allocator_arg, allocator<int>{}, std::move(t1a));
+ VERIFY( std::get<0>(t1b).v );
+
+ const tuple<A, int> t2a0;
+ tuple<B, int> t2b0(allocator_arg, allocator<int>{}, std::move(t2a0));
+ VERIFY( std::get<0>(t2b0).v );
+
+ const tuple<int, A> t2a1;
+ tuple<int, B> t2b1(allocator_arg, allocator<int>{}, std::move(t2a1));
+ VERIFY( std::get<1>(t2b1).v );
+
+ const tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0(allocator_arg, allocator<int>{}, std::move(t3a0));
+ VERIFY( std::get<0>(t3b0).v );
+
+ const tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1(allocator_arg, allocator<int>{}, std::move(t3a1));
+ VERIFY( std::get<1>(t3b1).v );
+
+ const tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2(allocator_arg, allocator<int>{}, std::move(t3a2));
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class Alloc, class U1, class U2>
+ // constexpr explicit(true)
+ // tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&&);
+
+ tuple<B, int> p2b0(allocator_arg, allocator<int>{}, std::move(t2a0));
+ VERIFY( std::get<0>(p2b0).v );
+
+ tuple<int, B> p2b1(allocator_arg, allocator<int>{}, std::move(t2a1));
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+ }
+};
+
+constexpr bool
+test05()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(const A&) const { v = true; return *this; }
+ };
+
+ // template<class... UTypes>
+ // constexpr const tuple& operator=(const tuple<UTypes...>&) const;
+
+ const tuple<A> t1a;
+ const tuple<B> t1b;
+ t1b = t1a;
+ VERIFY( std::get<0>(t1b).v );
+
+ const tuple<A, A> t2a;
+ const tuple<B, B> t2b;
+ t2b = t2a;
+ VERIFY( std::get<0>(t2b).v );
+ VERIFY( std::get<1>(t2b).v );
+
+ const tuple<A, A, A> t3a;
+ const tuple<B, B, B> t3b;
+ t3b = t3a;
+ VERIFY( std::get<0>(t3b).v );
+ VERIFY( std::get<1>(t3b).v );
+ VERIFY( std::get<2>(t3b).v );
+
+ // template<class U1, class U2>
+ // constexpr const tuple& operator=(const pair<U1, U2>&) const;
+
+ const pair<A, A> p2a;
+ const tuple<B, B> p2b;
+ p2b = p2a;
+
+ return true;
+}
+
+constexpr bool
+test06()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(A&&) const { v = true; return *this; }
+ };
+
+ // template<class... UTypes>
+ // constexpr const tuple& operator=(tuple<UTypes...>&&) const;
+
+ tuple<A> t1a;
+ const tuple<B> t1b;
+ t1b = std::move(t1a);
+ VERIFY( std::get<0>(t1b).v );
+
+ tuple<A, A> t2a;
+ const tuple<B, B> t2b;
+ t2b = std::move(t2a);
+ VERIFY( std::get<0>(t2b).v );
+ VERIFY( std::get<1>(t2b).v );
+
+ tuple<A, A, A> t3a;
+ const tuple<B, B, B> t3b;
+ t3b = std::move(t3a);
+ VERIFY( std::get<0>(t3b).v );
+ VERIFY( std::get<1>(t3b).v );
+ VERIFY( std::get<2>(t3b).v );
+
+ // template<class U1, class U2>
+ // constexpr const tuple& operator=(pair<U1, U2>&&) const;
+
+ pair<A, A> p2a;
+ const tuple<B, B> p2b;
+ p2b = std::move(p2a);
+
+ return true;
+}
+
+constexpr bool
+test07()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(const B&) const { v = true; return *this; }
+ };
+
+ // constexpr const tuple& operator=(const tuple&) const;
+
+ const tuple<B> t1a;
+ const tuple<B> t1b;
+ t1b = t1a;
+ VERIFY( std::get<0>(t1b).v );
+
+ const tuple<B, B> t2a;
+ const tuple<B, B> t2b;
+ t2b = t2a;
+ VERIFY( std::get<0>(t2b).v );
+ VERIFY( std::get<1>(t2b).v );
+
+ const tuple<B, B, B> t3a;
+ const tuple<B, B, B> t3b;
+ t3b = t3a;
+ VERIFY( std::get<0>(t3b).v );
+ VERIFY( std::get<1>(t3b).v );
+ VERIFY( std::get<2>(t3b).v );
+
+ return true;
+}
+
+constexpr bool
+test08()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(B&&) const { v = true; return *this; }
+ };
+
+ // constexpr const tuple& operator=(tuple&&) const;
+
+ tuple<B> t1a;
+ const tuple<B> t1b;
+ t1b = std::move(t1a);
+ VERIFY( std::get<0>(t1b).v );
+
+ tuple<B, B> t2a;
+ const tuple<B, B> t2b;
+ t2b = std::move(t2a);
+ VERIFY( std::get<0>(t2b).v );
+ VERIFY( std::get<1>(t2b).v );
+
+ tuple<B, B, B> t3a;
+ const tuple<B, B, B> t3b;
+ t3b = std::move(t3a);
+ VERIFY( std::get<0>(t3b).v );
+ VERIFY( std::get<1>(t3b).v );
+ VERIFY( std::get<2>(t3b).v );
+
+ return true;
+}
+
+struct S
+{
+ mutable int v = 0;
+ friend constexpr void swap(S&& x, S&& y) = delete;
+ friend constexpr void swap(const S& x, const S& y) { ++x.v; ++y.v; }
+};
+
+constexpr bool
+test09()
+{
+ const tuple<S> t1, u1;
+ std::swap(t1, u1);
+ VERIFY( std::get<0>(t1).v == 1 );
+ VERIFY( std::get<0>(u1).v == 1 );
+
+ const tuple<S, S> t2, u2;
+ std::swap(t2, u2);
+ VERIFY( std::get<0>(t2).v == 1 );
+ VERIFY( std::get<0>(u2).v == 1 );
+ VERIFY( std::get<1>(t2).v == 1 );
+ VERIFY( std::get<1>(u2).v == 1 );
+
+ const tuple<S, S, S> t3, u3;
+ std::swap(t3, u3);
+ VERIFY( std::get<0>(t3).v == 1 );
+ VERIFY( std::get<0>(u3).v == 1 );
+ VERIFY( std::get<1>(t3).v == 1 );
+ VERIFY( std::get<1>(u3).v == 1 );
+ VERIFY( std::get<2>(t3).v == 1 );
+ VERIFY( std::get<2>(u3).v == 1 );
+
+ static_assert(!std::is_swappable_v<const tuple<A>&>);
+
+ return true;
+}
+
+int
+main()
+{
+ static_assert(test01());
+ static_assert(alloc::test01());
+ static_assert(test02());
+ static_assert(alloc::test02());
+ static_assert(test03());
+ static_assert(alloc::test03());
+ static_assert(test04());
+ static_assert(alloc::test04());
+ // FIXME: G++ doesn't support reading mutable members during constexpr (PR c++/92505).
+ test05();
+ test06();
+ test07();
+ test08();
+ test09();
+}
new file mode 100644
@@ -0,0 +1,26 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+// { dg-xfail-if "not supported" { debug_mode } }
+
+#include <vector>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test01()
+{
+ // P2321R2
+ // constexpr const reference& vector<bool>::operator=(bool x) const noexcept;
+
+ std::vector<bool> v(1);
+ const auto e = v[0];
+ e = true;
+ VERIFY( v[0] );
+
+ return true;
+}
+
+int
+main()
+{
+ static_assert(test01());
+}