This implements the non-<ranges> changes from P2321R2, which primarily consist of new converting constructors, assignment operator and swap overloads for std::pair and std::tuple.
Tested on x86_64-pc-linux-gnu, does this look OK for trunk? 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/p2321.cc: New test. * testsuite/20_util/tuple/p2321.cc: New test. * testsuite/23_containers/vector/bool/element_access/1.cc: New test. --- libstdc++-v3/include/bits/stl_bvector.h | 12 + libstdc++-v3/include/bits/stl_pair.h | 118 +++- .../include/bits/uses_allocator_args.h | 41 ++ libstdc++-v3/include/std/tuple | 416 +++++++++++ libstdc++-v3/testsuite/20_util/pair/p2321.cc | 208 ++++++ libstdc++-v3/testsuite/20_util/tuple/p2321.cc | 664 ++++++++++++++++++ .../vector/bool/element_access/1.cc | 26 + 7 files changed, 1480 insertions(+), 5 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/pair/p2321.cc create mode 100644 libstdc++-v3/testsuite/20_util/tuple/p2321.cc create mode 100644 libstdc++-v3/testsuite/23_containers/vector/bool/element_access/1.cc diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h index d256af40f40..c5fd19e7309 100644 --- a/libstdc++-v3/include/bits/stl_bvector.h +++ b/libstdc++-v3/include/bits/stl_bvector.h @@ -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 diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h index 831e770d54b..d0efba635bc 100644 --- a/libstdc++-v3/include/bits/stl_pair.h +++ b/libstdc++-v3/include/bits/stl_pair.h @@ -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 diff --git a/libstdc++-v3/include/bits/uses_allocator_args.h b/libstdc++-v3/include/bits/uses_allocator_args.h index 09cdbf1aaa8..3528e4cc4fa 100644 --- a/libstdc++-v3/include/bits/uses_allocator_args.h +++ b/libstdc++-v3/include/bits/uses_allocator_args.h @@ -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) diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index d0c168fd7e2..812e70d17be 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -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 diff --git a/libstdc++-v3/testsuite/20_util/pair/p2321.cc b/libstdc++-v3/testsuite/20_util/pair/p2321.cc new file mode 100644 index 00000000000..4f436ee03d6 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/pair/p2321.cc @@ -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(); +} diff --git a/libstdc++-v3/testsuite/20_util/tuple/p2321.cc b/libstdc++-v3/testsuite/20_util/tuple/p2321.cc new file mode 100644 index 00000000000..80fc23cf9d4 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/p2321.cc @@ -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(); +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/1.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/1.cc new file mode 100644 index 00000000000..9016c026b33 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/1.cc @@ -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()); +} -- 2.37.2.382.g795ea8776b