libstdc++: Implement std::pair/tuple/misc enhancements from P2321R2

This implements the non-<ranges> changes from P2321R2, which primarily
consist of additional converting constructors, assignment operator and
swap overloads for std::pair and std::tuple.

libstdc++-v3/ChangeLog:

	* include/bits/stl_bvector.h (_Bit_reference::operator=): Define
	const overload for C++23 as per P2321R2.
	* include/bits/stl_pair.h (pair::swap): Likewise.
	(pair::pair): Define additional converting constructors for
	C++23 as per P2321R2.
	(pair::operator=): Define const overloads for C++23 as per
	P2321R2.
	(swap): Define overload taking const pair& for C++23 as per
	P2321R2.
	(basic_common_reference): Define partial specialization for
	pair for C++23 as per P2321R2.
	(common_type): Likewise.
	* include/bits/uses_allocator_args.h
	(uses_allocator_construction_args): Define additional pair
	overloads for C++23 as per P2321R2.
	* include/std/tuple (_Tuple_impl::_Tuple_impl): Define
	additional converting constructors for C++23 as per P2321R2.
	(_Tuple_impl::_M_assign): Define const overloads for C++23
	as per P2321R2.
	(_Tuple_impl::_M_swap): Likewise.
	(tuple::__constructible): Define as a convenient renaming of
	_TCC<true>::__constructible.
	(tuple::__convertible): As above but for _TCC<true>::__convertible.
	(tuple::tuple): Define additional converting constructors for
	C++23 as per P2321R2.
	(tuple::operator=): Define const overloads for C++23 as per
	P2321R2.
	(tuple::swap): Likewise.
	(basic_common_reference): Define partial specialization for
	tuple for C++23 as per P2321R2.
	(common_type): Likewise.
	* testsuite/20_util/pair/p2321r2.cc: New test.
	* testsuite/20_util/tuple/p2321r2.cc: New test.
	* testsuite/23_containers/vector/bool/element_access/1.cc: New test.
This commit is contained in:
Patrick Palka 2022-08-23 13:42:37 -04:00
parent 02f6b405f0
commit 72886fcc62
7 changed files with 1479 additions and 5 deletions

View file

@ -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 // C++23
_GLIBCXX20_CONSTEXPR
_Bit_reference&
operator=(const _Bit_reference& __x) _GLIBCXX_NOEXCEPT

View file

@ -212,6 +212,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
swap(second, __p.second);
}
#if __cplusplus > 202002L
constexpr void
swap(const pair& __p) const
noexcept(__and_v<__is_nothrow_swappable<const _T1>,
__is_nothrow_swappable<const _T2>>)
{
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 +295,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 +304,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 +314,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 // C++23
private:
/// @cond undocumented
template<typename _U1, typename _U2>
@ -349,7 +382,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 +394,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 +405,55 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
second = std::forward<_U2>(__p.second);
return *this;
}
#else
#if __cplusplus > 202002L
/// Copy assignment operator (const)
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 (const)
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 +791,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
noexcept(noexcept(__x.swap(__y)))
{ __x.swap(__y); }
#if __cplusplus > 202002L
template<typename _T1, typename _T2>
requires is_swappable_v<const _T1> && is_swappable_v<const _T2>
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 +1008,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 // C++23
#endif // C++14
/// @}
#endif // C++11

View file

@ -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 // C++23
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 // C++23
template<typename _Tp, typename _Alloc, typename... _Args>
constexpr _Tp
make_obj_using_allocator(const _Alloc& __a, _Args&&... __args)

View file

@ -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 // C++23
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 // C++23
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 // C++23
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 // C++23
};
// 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 // C++23
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 // C++23
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 // C++23
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 // C++23
};
// Concept utility functions, reused in conditionally-explicit
@ -712,6 +842,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...>::value;
template<typename... _Args>
static constexpr bool __convertible
= _TCC<true>::template __convertible<_Args...>::value;
#endif // C++23
public:
template<typename _Dummy = void,
_ImplicitDefaultCtor<is_void<_Dummy>::value> = true>
@ -799,6 +939,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 // C++23
// Allocator-extended constructors.
template<typename _Alloc,
@ -897,6 +1060,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 // C++23
// tuple assignment
_GLIBCXX20_CONSTEXPR
@ -941,12 +1130,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 // C++23
// 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_v<__is_nothrow_swappable<const _Elements>...>)
{ _Inherited::_M_swap(__in); }
#endif // C++23
};
#if __cpp_deduction_guides >= 201606
@ -969,6 +1203,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;
@ -1048,6 +1285,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>::value;
template<typename _U1, typename _U2>
static constexpr bool __convertible
= _TCC<true>::template __convertible<_U1, _U2>::value;
#endif // C++23
public:
template<bool _Dummy = true,
_ImplicitDefaultCtor<_Dummy, _T1, _T2> = true>
@ -1123,6 +1370,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 // C++23
template<typename _U1, typename _U2,
_ImplicitCtor<true, const _U1&, const _U2&> = true>
constexpr
@ -1153,6 +1418,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 // C++23
// Allocator-extended constructors.
template<typename _Alloc,
@ -1236,6 +1520,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 // C++23
template<typename _Alloc, typename _U1, typename _U2,
_ImplicitCtor<true, const _U1&, const _U2&> = true>
_GLIBCXX20_CONSTEXPR
@ -1266,6 +1572,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 // C++23
// Tuple assignment.
_GLIBCXX20_CONSTEXPR
@ -1310,6 +1634,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 // C++23
template<typename _U1, typename _U2>
_GLIBCXX20_CONSTEXPR
__enable_if_t<__assignable<const _U1&, const _U2&>(), tuple&>
@ -1332,12 +1694,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 // C++23
_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_v<__is_nothrow_swappable<const _T1>,
__is_nothrow_swappable<const _T2>>)
{ _Inherited::_M_swap(__in); }
#endif // C++23
};
@ -1765,6 +2159,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
noexcept(noexcept(__x.swap(__y)))
{ __x.swap(__y); }
#if __cplusplus > 202002L
template<typename... _Elements>
requires (is_swappable_v<const _Elements> && ...)
constexpr void
swap(const tuple<_Elements...>& __x, const tuple<_Elements...>& __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... _Elements>
_GLIBCXX20_CONSTEXPR
@ -1889,6 +2292,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 // C++23
/// @}
_GLIBCXX_END_NAMESPACE_VERSION

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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());
}