On Wed, 24 Jan 2024, Patrick Palka wrote: > On Wed, 24 Jan 2024, Patrick Palka wrote: > > > On Wed, 24 Jan 2024, Jonathan Wakely wrote: > > > > > On Wed, 24 Jan 2024 at 15:24, Patrick Palka <ppa...@redhat.com> wrote: > > > > > > > > On Wed, 24 Jan 2024, Jonathan Wakely wrote: > > > > > > > > > On Tue, 23 Jan 2024 at 23:54, Patrick Palka wrote: > > > > > > diff --git a/libstdc++-v3/include/bits/stl_pair.h > > > > > > b/libstdc++-v3/include/bits/stl_pair.h > > > > > > index b81b479ad43..a9b20fbe7ca 100644 > > > > > > --- a/libstdc++-v3/include/bits/stl_pair.h > > > > > > +++ b/libstdc++-v3/include/bits/stl_pair.h > > > > > > @@ -85,12 +85,70 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > /// @cond undocumented > > > > > > > > > > > > // Forward declarations. > > > > > > + template<typename, typename> > > > > > > + struct pair; > > > > > > > > > > We have a compiler bug where a forward declaration without template > > > > > parameter names causes bad diagnostics later. The compiler seems to > > > > > try to use the parameter names from the first decl it sees, so we end > > > > > up with things like <template-argument-1-1> even when there's a name > > > > > available at the site of the actual error. So I think we should name > > > > > these _T1 and _T2 here. > > > > > > > > Will fix. > > > > > > > > > > > > > > > + > > > > > > template<typename...> > > > > > > class tuple; > > > > > > > > > > > > + // Declarations of std::array and its std::get overloads, so that > > > > > > + // std::tuple_cat can use them if <tuple> is included before > > > > > > <array>. > > > > > > + // We also declare the other std::get overloads here so that > > > > > > they're > > > > > > + // visible to the P2165R4 tuple-like constructors of pair and > > > > > > tuple. > > > > > > + template<typename _Tp, size_t _Nm> > > > > > > + struct array; > > > > > > + > > > > > > template<size_t...> > > > > > > struct _Index_tuple; > > > > > > > > > > > > + template<size_t _Int, class _Tp1, class _Tp2> > > > > > > + constexpr typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type& > > > > > > + get(pair<_Tp1, _Tp2>& __in) noexcept; > > > > > > + > > > > > > + template<size_t _Int, class _Tp1, class _Tp2> > > > > > > + constexpr typename tuple_element<_Int, pair<_Tp1, > > > > > > _Tp2>>::type&& > > > > > > + get(pair<_Tp1, _Tp2>&& __in) noexcept; > > > > > > + > > > > > > + template<size_t _Int, class _Tp1, class _Tp2> > > > > > > + constexpr const typename tuple_element<_Int, pair<_Tp1, > > > > > > _Tp2>>::type& > > > > > > + get(const pair<_Tp1, _Tp2>& __in) noexcept; > > > > > > + > > > > > > + template<size_t _Int, class _Tp1, class _Tp2> > > > > > > + constexpr const typename tuple_element<_Int, pair<_Tp1, > > > > > > _Tp2>>::type&& > > > > > > + get(const pair<_Tp1, _Tp2>&& __in) noexcept; > > > > > > + > > > > > > + template<size_t __i, typename... _Elements> > > > > > > + constexpr __tuple_element_t<__i, tuple<_Elements...>>& > > > > > > + get(tuple<_Elements...>& __t) noexcept; > > > > > > + > > > > > > + template<size_t __i, typename... _Elements> > > > > > > + constexpr const __tuple_element_t<__i, tuple<_Elements...>>& > > > > > > + get(const tuple<_Elements...>& __t) noexcept; > > > > > > + > > > > > > + template<size_t __i, typename... _Elements> > > > > > > + constexpr __tuple_element_t<__i, tuple<_Elements...>>&& > > > > > > + get(tuple<_Elements...>&& __t) noexcept; > > > > > > + > > > > > > + template<size_t __i, typename... _Elements> > > > > > > + constexpr const __tuple_element_t<__i, tuple<_Elements...>>&& > > > > > > + get(const tuple<_Elements...>&& __t) noexcept; > > > > > > + > > > > > > + template<size_t _Int, typename _Tp, size_t _Nm> > > > > > > + constexpr _Tp& > > > > > > + get(array<_Tp, _Nm>&) noexcept; > > > > > > + > > > > > > + template<size_t _Int, typename _Tp, size_t _Nm> > > > > > > + constexpr _Tp&& > > > > > > + get(array<_Tp, _Nm>&&) noexcept; > > > > > > + > > > > > > + template<size_t _Int, typename _Tp, size_t _Nm> > > > > > > + constexpr const _Tp& > > > > > > + get(const array<_Tp, _Nm>&) noexcept; > > > > > > + > > > > > > + template<size_t _Int, typename _Tp, size_t _Nm> > > > > > > + constexpr const _Tp&& > > > > > > + get(const array<_Tp, _Nm>&&) noexcept; > > > > > > + > > > > > > #if ! __cpp_lib_concepts > > > > > > // Concept utility functions, reused in conditionally-explicit > > > > > > // constructors. > > > > > > @@ -159,6 +217,46 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > #endif // lib concepts > > > > > > #endif // C++11 > > > > > > > > > > > > +#if __glibcxx_tuple_like // >= C++23 > > > > > > + template<typename _Tp> > > > > > > + inline constexpr bool __is_tuple_v = false; > > > > > > + > > > > > > + template<typename... _Ts> > > > > > > + inline constexpr bool __is_tuple_v<tuple<_Ts...>> = true; > > > > > > + > > > > > > + // TODO: Reuse __is_tuple_like from <type_traits>? > > > > > > + template<typename _Tp> > > > > > > + inline constexpr bool __is_tuple_like_v = false; > > > > > > + > > > > > > + template<typename... _Elements> > > > > > > + inline constexpr bool __is_tuple_like_v<tuple<_Elements...>> = > > > > > > true; > > > > > > + > > > > > > + template<typename _T1, typename _T2> > > > > > > + inline constexpr bool __is_tuple_like_v<pair<_T1, _T2>> = true; > > > > > > + > > > > > > + template<typename _Tp, size_t _Nm> > > > > > > + inline constexpr bool __is_tuple_like_v<array<_Tp, _Nm>> = > > > > > > true; > > > > > > + > > > > > > + // __is_tuple_like_v<subrange> is defined in > > > > > > <bits/ranges_util.h>. > > > > > > + > > > > > > + template<typename _Tp> > > > > > > + concept __tuple_like = __is_tuple_like_v<remove_cvref_t<_Tp>>; > > > > > > + > > > > > > + template<typename _Tp> > > > > > > + concept __pair_like = __tuple_like<_Tp> && > > > > > > tuple_size_v<remove_cvref_t<_Tp>> == 2; > > > > > > + > > > > > > + template<typename _Tp, typename _Tuple> > > > > > > + concept __eligible_tuple_like > > > > > > + = __detail::__different_from<_Tp, _Tuple> && > > > > > > __tuple_like<_Tp> > > > > > > + && (tuple_size_v<remove_cvref_t<_Tp>> == > > > > > > tuple_size_v<_Tuple>) > > > > > > + && !ranges::__detail::__is_subrange<remove_cvref_t<_Tp>>; > > > > > > + > > > > > > + template<typename _Tp, typename _Pair> > > > > > > + concept __eligible_pair_like > > > > > > + = __detail::__different_from<_Tp, _Pair> && __pair_like<_Tp> > > > > > > + && !ranges::__detail::__is_subrange<remove_cvref_t<_Tp>>; > > > > > > +#endif // C++23 > > > > > > + > > > > > > template<typename _U1, typename _U2> class __pair_base > > > > > > { > > > > > > #if __cplusplus >= 201103L && ! __cpp_lib_concepts > > > > > > @@ -295,6 +393,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > return false; > > > > > > #endif > > > > > > } > > > > > > + > > > > > > +#if __glibcxx_tuple_like // >= C++23 > > > > > > + template<typename _UPair> > > > > > > + static constexpr bool > > > > > > + _S_constructible_from_pair_like() > > > > > > + { > > > > > > + return > > > > > > _S_constructible<decltype(std::get<0>(std::declval<_UPair>())), > > > > > > + > > > > > > decltype(std::get<1>(std::declval<_UPair>()))>(); > > > > > > + } > > > > > > + > > > > > > + template<typename _UPair> > > > > > > + static constexpr bool > > > > > > + _S_convertible_from_pair_like() > > > > > > + { > > > > > > + return > > > > > > _S_convertible<decltype(std::get<0>(std::declval<_UPair>())), > > > > > > + > > > > > > decltype(std::get<1>(std::declval<_UPair>()))>(); > > > > > > + } > > > > > > +#endif // C++23 > > > > > > /// @endcond > > > > > > > > > > > > public: > > > > > > @@ -393,6 +509,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > pair(const pair<_U1, _U2>&&) = delete; > > > > > > #endif // C++23 > > > > > > > > > > > > +#if __glibcxx_tuple_like // >= C++23 > > > > > > + template<__eligible_pair_like<pair> _UPair> > > > > > > + requires (_S_constructible_from_pair_like<_UPair>()) > > > > > > + constexpr explicit(!_S_convertible_from_pair_like<_UPair>()) > > > > > > + pair(_UPair&& __p) > > > > > > + : first(std::get<0>(std::forward<_UPair>(__p))), > > > > > > + second(std::get<1>(std::forward<_UPair>(__p))) > > > > > > + { } > > > > > > +#endif // C++23 > > > > > > > > > > I think this needs to be constrained with !_S_dangles<...>() and we > > > > > need a deleted overload with the same constraints, except for > > > > > _S_dangles being true. > > > > > > > > > > And that should be covered by a test. > > > > > > > > Oops, will fix. Should the deleted overloads carry over the > > > > conditionally explicit specifier? I noticed pair's deleted overloads > > > > do, but tuple's overloads don't. > > > > > > Huh, oops. Does an explicit ctor participate in overload resolution > > > and then get checked if it's usable, or is it remove from overload > > > resolution earlier? > > > It looks like I decided the answer was the latter for pair and the > > > former for tuple. > > > > AFAICT if we know the explicitness of the ctor early (either because the > > ctor is a non-template or it has a non-dependent explicit-spec), then we > > remove it from the overload set early, in which case it'd be useful to > > give the deleted ctor the right explicit-spec to avoid unecessary > > constraint checking etc. > > > > Otherwise, for ctor templates with a dependent explicit-spec such as > > these tuple/pair ones, we must wait until after deduction to check > > explicitness which means constraints are checked first. And by then > > we already know that __dangles is true, so we presumably want overload > > resolution to fail regardless. Whether that's due to selecting a > > (viable) deleted non-explicit ctor (if we omit the explicit-spec) or due > > to there being no viable non-explicit ctor (if we carry over the > > explicit-spec) shouldn't make a difference, I think? > > > > So it seems unnecessary to give these deleted overloads an > > explicit-spec; it wouldn't be considered unless overload resolution > > is destined to fail anyway. > > > > I'm not totally confident about this assessment though so I'll > > just carry over the explicit-spec for now. > > I ended up hedging my bets and including the explicit-spec in the > deleted ctors of pair and omitting it in those of tuple, so that > we continue to be locally consistent. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > diff --git a/libstdc++-v3/include/std/tuple > > > > > > b/libstdc++-v3/include/std/tuple > > > > > > index be92f1eb973..182f3cc5e6a 100644 > > > > > > --- a/libstdc++-v3/include/std/tuple > > > > > > +++ b/libstdc++-v3/include/std/tuple > > > > > > @@ -50,6 +50,7 @@ > > > > > > #define __glibcxx_want_apply > > > > > > #define __glibcxx_want_make_from_tuple > > > > > > #define __glibcxx_want_ranges_zip > > > > > > +#define __glibcxx_want_tuple_like > > > > > > #include <bits/version.h> > > > > > > > > > > > > namespace std _GLIBCXX_VISIBILITY(default) > > > > > > @@ -246,6 +247,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > _Head _M_head_impl; > > > > > > }; > > > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > > + struct __tuple_like_tag_t { explicit __tuple_like_tag_t() = > > > > > > default; }; > > > > > > + > > > > > > + // Forward declared for use by the operator<=> overload for > > > > > > tuple-like types. > > > > > > + template<typename _Cat, typename _Tp, typename _Up> > > > > > > + constexpr _Cat > > > > > > + __tuple_cmp(const _Tp&, const _Up&, index_sequence<>); > > > > > > + > > > > > > + template<typename _Cat, typename _Tp, typename _Up, > > > > > > + size_t _Idx0, size_t... _Idxs> > > > > > > + constexpr _Cat > > > > > > + __tuple_cmp(const _Tp& __t, const _Up& __u, > > > > > > + index_sequence<_Idx0, _Idxs...>); > > > > > > +#endif // C++23 > > > > > > + > > > > > > /** > > > > > > * Contains the actual implementation of the @c tuple template, > > > > > > stored > > > > > > * as a recursive inheritance hierarchy from the first element > > > > > > (most > > > > > > @@ -342,6 +358,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > { } > > > > > > #endif // C++23 > > > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > > + template<typename _UTuple, size_t... _Is> > > > > > > + constexpr > > > > > > + _Tuple_impl(__tuple_like_tag_t, _UTuple&& __u, > > > > > > index_sequence<_Is...>) > > > > > > + : _Tuple_impl(std::get<_Is>(std::forward<_UTuple>(__u))...) > > > > > > + { } > > > > > > +#endif // C++23 > > > > > > + > > > > > > template<typename _Alloc> > > > > > > _GLIBCXX20_CONSTEXPR > > > > > > _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a) > > > > > > @@ -428,6 +452,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > { } > > > > > > #endif // C++23 > > > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > > + template<typename _Alloc, typename _UTuple, size_t... _Is> > > > > > > + constexpr > > > > > > + _Tuple_impl(__tuple_like_tag_t, allocator_arg_t __tag, > > > > > > const _Alloc& __a, > > > > > > + _UTuple&& __u, index_sequence<_Is...>) > > > > > > + : _Tuple_impl(__tag, __a, > > > > > > std::get<_Is>(std::forward<_UTuple>(__u))...) > > > > > > + { } > > > > > > +#endif // C++23 > > > > > > + > > > > > > template<typename... _UElements> > > > > > > _GLIBCXX20_CONSTEXPR > > > > > > void > > > > > > @@ -470,6 +503,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > } > > > > > > #endif // C++23 > > > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > > + template<typename _UTuple> > > > > > > + constexpr void > > > > > > + _M_assign(__tuple_like_tag_t __tag, _UTuple&& __u) > > > > > > + { > > > > > > + _M_head(*this) = > > > > > > std::get<_Idx>(std::forward<_UTuple>(__u)); > > > > > > + _M_tail(*this)._M_assign(__tag, > > > > > > std::forward<_UTuple>(__u)); > > > > > > + } > > > > > > + > > > > > > + template<typename _UTuple> > > > > > > + constexpr void > > > > > > + _M_assign(__tuple_like_tag_t __tag, _UTuple&& __u) const > > > > > > + { > > > > > > + _M_head(*this) = > > > > > > std::get<_Idx>(std::forward<_UTuple>(__u)); > > > > > > + _M_tail(*this)._M_assign(__tag, > > > > > > std::forward<_UTuple>(__u)); > > > > > > + } > > > > > > +#endif // C++23 > > > > > > + > > > > > > protected: > > > > > > _GLIBCXX20_CONSTEXPR > > > > > > void > > > > > > @@ -563,6 +614,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > { } > > > > > > #endif // C++23 > > > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > > + template<typename _UTuple> > > > > > > + constexpr > > > > > > + _Tuple_impl(__tuple_like_tag_t, _UTuple&& __u, > > > > > > index_sequence<0>) > > > > > > + : _Tuple_impl(std::get<0>(std::forward<_UTuple>(__u))) > > > > > > + { } > > > > > > +#endif // C++23 > > > > > > + > > > > > > template<typename _Alloc> > > > > > > _GLIBCXX20_CONSTEXPR > > > > > > _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a) > > > > > > @@ -633,6 +692,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > { } > > > > > > #endif // C++23 > > > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > > + template<typename _Alloc, typename _UTuple> > > > > > > + constexpr > > > > > > + _Tuple_impl(__tuple_like_tag_t, allocator_arg_t __tag, > > > > > > const _Alloc& __a, > > > > > > + _UTuple&& __u, index_sequence<0>) > > > > > > + : _Tuple_impl(__tag, __a, > > > > > > std::get<0>(std::forward<_UTuple>(__u))) > > > > > > + { } > > > > > > +#endif // C++23 > > > > > > + > > > > > > template<typename _UHead> > > > > > > _GLIBCXX20_CONSTEXPR > > > > > > void > > > > > > @@ -667,6 +735,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > } > > > > > > #endif // C++23 > > > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > > + template<typename _UTuple> > > > > > > + constexpr void > > > > > > + _M_assign(__tuple_like_tag_t, _UTuple&& __u) > > > > > > + { _M_head(*this) = > > > > > > std::get<_Idx>(std::forward<_UTuple>(__u)); } > > > > > > + > > > > > > + template<typename _UTuple> > > > > > > + constexpr void > > > > > > + _M_assign(__tuple_like_tag_t, _UTuple&& __u) const > > > > > > + { _M_head(*this) = > > > > > > std::get<_Idx>(std::forward<_UTuple>(__u)); } > > > > > > +#endif // C++23 > > > > > > + > > > > > > protected: > > > > > > _GLIBCXX20_CONSTEXPR > > > > > > void > > > > > > @@ -846,6 +926,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > #endif > > > > > > } > > > > > > > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > > + template<typename _UTuple> > > > > > > + static consteval bool > > > > > > + __constructible_from_tuple_like() > > > > > > + { > > > > > > + return []<size_t... _Is>(index_sequence<_Is...>) { > > > > > > + return > > > > > > __constructible<decltype(std::get<_Is>(std::declval<_UTuple>()))...>(); > > > > > > + }(make_index_sequence<sizeof...(_Elements)>{}); > > > > > > + } > > > > > > + > > > > > > + template<typename _UTuple> > > > > > > + static consteval bool > > > > > > + __convertible_from_tuple_like() > > > > > > + { > > > > > > + return []<size_t... _Is>(index_sequence<_Is...>) { > > > > > > + return > > > > > > __convertible<decltype(std::get<_Is>(std::declval<_UTuple>()))...>(); > > > > > > + }(make_index_sequence<sizeof...(_Elements)>{}); > > > > > > > > > > These new functions can use index_sequence_for<_Elements...>{} here, > > > > > so you don't need the sizeof.... > > > > > That applies several times below as well. > > > > > > > > > > I think it's semantically identical, just a little shorter. I don't > > > > > know if there's any compilation speed benefit either way. Maybe > > > > > sizeof...(_Elements) is cheaper than expanding the pack into the > > > > > index_sequence_for alias template? > > It probably a little cheaper to use make_index_sequence directly, but I > just didn't know about index_sequence_for :) Consider that changed. > > > > > > > > > > > > > > > > > + } > > > > > > +#endif // C++23 > > > > > > + > > > > > > public: > > > > > > constexpr > > > > > > > > > > > > explicit(!(__is_implicitly_default_constructible_v<_Elements> && > > > > > > ...)) > > > > > > @@ -1016,10 +1116,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > tuple(const pair<_U1, _U2>&&) = delete; > > > > > > #endif // C++23 > > > > > > > > > > > > -#if 0 && __cpp_lib_tuple_like // >= C++23 > > > > > > - template<__tuple_like _UTuple> > > > > > > - constexpr explicit(...) > > > > > > - tuple(_UTuple&& __u); > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > > + template<__eligible_tuple_like<tuple> _UTuple> > > > > > > + requires (__constructible_from_tuple_like<_UTuple>()) > > > > > > + && (!__use_other_ctor<_UTuple>()) > > > > > > + constexpr > > > > > > explicit(!__convertible_from_tuple_like<_UTuple>()) > > > > > > + tuple(_UTuple&& __u) > > > > > > + : _Inherited(__tuple_like_tag_t{}, > > > > > > + std::forward<_UTuple>(__u), > > > > > > + make_index_sequence<sizeof...(_Elements)>{}) > > > > > > + { } > > > > > > #endif // C++23 > > > > > > > > > > > > // Allocator-extended constructors. > > > > > > @@ -1202,10 +1308,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > > > > > tuple(allocator_arg_t, const _Alloc&, const pair<_U1, > > > > > > _U2>&&) = delete; > > > > > > #endif // C++23 > > > > > > > > > > > > -#if 0 && __cpp_lib_tuple_like // >= C++23 > > > > > > - template<typename _Alloc, __tuple_like _UTuple> > > > > > > - constexpr explicit(...) > > > > > > - tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& > > > > > > __u); > > > > > > +#if __cpp_lib_tuple_like // >= C++23 > > > > > > + template<typename _Alloc, __eligible_tuple_like<tuple> > > > > > > _UTuple> > > > > > > + requires (__constructible_from_tuple_like<_UTuple>()) > > > > > > + && (!__use_other_ctor<_UTuple>()) > > > > > > + constexpr > > > > > > explicit(!__convertible_from_tuple_like<_UTuple>()) > > > > > > + tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& > > > > > > __u) > > > > > > + : _Inherited(__tuple_like_tag_t{}, > > > > > > + __tag, __a, std::forward<_UTuple>(__u), > > > > > > + make_index_sequence<sizeof...(_Elements)>{}) > > > > > > + { } > > > > > > #endif // C++23 > > > > > > > > > > For some reason these two new constructors aren't deleted if they > > > > > create dangling refs. I don't know why. > > > > > > > > Hmm, seems like an oversight. Shall we proactively implement them? > > > > > > Yes, I think so. I can't see why we would want to permit a dangling > > > reference there. > > > > > > e.g. > > > std::array<long, 1> a{}; > > > std::tuple<const int&> t(a); > > > > Sounds good. > > In v2: > > * Named the template parameters of the forward declaration of pair. > * Added dangling checks for the new tuple and pair constructors > and corresponding tests. > * Replaced make_index_sequence with index_sequence_for where applicable.
Ping. > > -- >8 -- > > Subject: [PATCH 2/2] libstdc++: Implement P2165R4 changes to > std::pair/tuple/etc > > libstdc++-v3/ChangeLog: > > * include/bits/ranges_util.h (__detail::__pair_like): Don't > define in C++23 mode. > (__detail::__pair_like_convertible_from): Adjust as per P2165R4. > (__detail::__is_subrange<subrange>): Moved from <ranges>. > (__detail::__is_tuple_like_v<subrange>): Likewise. > * include/bits/stl_iterator.h: Include <bits/utility.h> for > C++23. > (__different_from): Move to <concepts>. > (__iter_key_t): Adjust for C++23 as per P2165R4. > (__iter_val_t): Likewise. > * include/bits/stl_pair.h (pair, array): Forward declare. > (get): Forward declare all overloads relevant to P2165R4 > tuple-like constructors. > (__is_tuple_v): Define for C++23. > (__is_tuple_like_v): Define for C++23. > (__tuple_like): Define for C++23 as per P2165R4. > (__pair_like): Define for C++23 as per P2165R4. > (__eligibile_tuple_like): Define for C++23. > (__eligibile_pair_like): Define for C++23. > (pair::_S_constructible_from_pair_like): Define for C++23. > (pair::_S_convertible_from_pair_like): Define for C++23. > (pair::_S_dangles_from_pair_like): Define for C++23. > (pair::pair): Define overloads taking a tuple-like type for > C++23 as per P2165R4. > (pair::_S_assignable_from_tuple_like): Define for C++23. > (pair::_S_const_assignable_from_tuple_like): Define for C++23. > (pair::operator=): Define overloads taking a tuple-like type for > C++23 as per P2165R4. > * include/bits/utility.h (ranges::__detail::__is_subrange): > Moved from <ranges>. > * include/bits/version.def (tuple_like): Define for C++23. > * include/bits/version.h: Regenerate. > * include/std/concepts (__different_from): Moved from > <bits/stl_iterator.h>. > (ranges::__swap::__adl_swap): Clarify which __detail namespace. > * include/std/map (__cpp_lib_tuple_like): Define C++23. > * include/std/ranges (__detail::__is_subrange): Moved to > <bits/utility.h>. > (__detail::__is_subrange<subrange>): Moved to <bits/ranges_util.h> > (__detail::__has_tuple_element): Adjust for C++23 as per P2165R4. > (__detail::__tuple_or_pair): Remove as per P2165R4. Replace all > uses with plain tuple as per P2165R4. > * include/std/tuple (__cpp_lib_tuple_like): Define for C++23. > (__tuple_like_tag_t): Define for C++23. > (__tuple_cmp): Forward declare for C++23. > (_Tuple_impl::_Tuple_impl): Define overloads taking > __tuple_like_tag_t and a tuple-like type for C++23. > (_Tuple_impl::_M_assign): Likewise. > (tuple::__constructible_from_tuple_like): Define for C++23. > (tuple::__convertible_from_tuple_like): Define for C++23. > (tuple::__dangles_from_tuple_like): Define for C++23. > (tuple::tuple): Define overloads taking a tuple-like type for > C++23 as per P2165R4. > (tuple::__assignable_from_tuple_like): Define for C++23. > (tuple::__const_assignable_from_tuple_like): Define for C++23. > (tuple::operator=): Define overloads taking a tuple-like type > for C++23 as per P2165R4. > (tuple::__tuple_like_common_comparison_category): Define for C++23. > (tuple::operator<=>): Define overload taking a tuple-like type > for C++23 as per P2165R4. > (array, get): Forward declarations moved to <bits/stl_pair.h>. > (tuple_cat): Constrain with __tuple_like for C++23 as per P2165R4. > (apply): Likewise. > (make_from_tuple): Likewise. > (__tuple_like_common_reference): Define for C++23. > (basic_common_reference): Adjust as per P2165R4. > (__tuple_like_common_type): Define for C++23. > (common_type): Adjust as per P2165R4. > * include/std/unordered_map (__cpp_lib_tuple_like): Define for > C++23. > * include/std/utility (__cpp_lib_tuple_like): Define for C++23. > * testsuite/std/ranges/zip/1.cc (test01): Adjust to handle pair > and 2-tuple interchangeably. > * testsuite/20_util/pair/p2165r4.cc: New test. > * testsuite/20_util/tuple/p2165r4.cc: New test. > --- > libstdc++-v3/include/bits/ranges_util.h | 17 +- > libstdc++-v3/include/bits/stl_iterator.h | 16 +- > libstdc++-v3/include/bits/stl_pair.h | 182 ++++++++++ > libstdc++-v3/include/bits/utility.h | 8 + > libstdc++-v3/include/bits/version.def | 8 + > libstdc++-v3/include/bits/version.h | 11 + > libstdc++-v3/include/std/concepts | 11 +- > libstdc++-v3/include/std/map | 1 + > libstdc++-v3/include/std/ranges | 48 +-- > libstdc++-v3/include/std/tuple | 323 ++++++++++++++--- > libstdc++-v3/include/std/unordered_map | 1 + > libstdc++-v3/include/std/utility | 1 + > .../testsuite/20_util/pair/p2165r4.cc | 173 +++++++++ > .../testsuite/20_util/tuple/p2165r4.cc | 335 ++++++++++++++++++ > libstdc++-v3/testsuite/std/ranges/zip/1.cc | 4 +- > 15 files changed, 1056 insertions(+), 83 deletions(-) > create mode 100644 libstdc++-v3/testsuite/20_util/pair/p2165r4.cc > create mode 100644 libstdc++-v3/testsuite/20_util/tuple/p2165r4.cc > > diff --git a/libstdc++-v3/include/bits/ranges_util.h > b/libstdc++-v3/include/bits/ranges_util.h > index bb04c49f044..9b79c3a229d 100644 > --- a/libstdc++-v3/include/bits/ranges_util.h > +++ b/libstdc++-v3/include/bits/ranges_util.h > @@ -224,6 +224,10 @@ namespace ranges > && !__uses_nonqualification_pointer_conversion<decay_t<_From>, > decay_t<_To>>; > > +#if __glibcxx_tuple_like // >= C++23 > + // P2165R4 version of __pair_like is defined in <bits/stl_pair.h>. > +#else > + // C++20 version of __pair_like from P2321R2. > template<typename _Tp> > concept __pair_like > = !is_reference_v<_Tp> && requires(_Tp __t) > @@ -235,10 +239,11 @@ namespace ranges > { get<0>(__t) } -> convertible_to<const tuple_element_t<0, _Tp>&>; > { get<1>(__t) } -> convertible_to<const tuple_element_t<1, _Tp>&>; > }; > +#endif > > template<typename _Tp, typename _Up, typename _Vp> > concept __pair_like_convertible_from > - = !range<_Tp> && __pair_like<_Tp> > + = !range<_Tp> && !is_reference_v<_Vp> && __pair_like<_Tp> > && constructible_from<_Tp, _Up, _Vp> > && __convertible_to_non_slicing<_Up, tuple_element_t<0, _Tp>> > && convertible_to<_Vp, tuple_element_t<1, _Tp>>; > @@ -463,8 +468,18 @@ namespace ranges > using borrowed_subrange_t = __conditional_t<borrowed_range<_Range>, > subrange<iterator_t<_Range>>, > dangling>; > + > + // __is_subrange is defined in <bits/utility.h>. > + template<typename _Iter, typename _Sent, subrange_kind _Kind> > + inline constexpr bool __detail::__is_subrange<subrange<_Iter, _Sent, > _Kind>> = true; > } // namespace ranges > > +#if __glibcxx_tuple_like // >= C++23 > + // __is_tuple_like_v is defined in <bits/stl_pair.h>. > + template<typename _It, typename _Sent, ranges::subrange_kind _Kind> > + inline constexpr bool __is_tuple_like_v<ranges::subrange<_It, _Sent, > _Kind>> = true; > +#endif > + > // The following ranges algorithms are used by <ranges>, and are defined here > // so that <ranges> can avoid including all of <bits/ranges_algo.h>. > namespace ranges > diff --git a/libstdc++-v3/include/bits/stl_iterator.h > b/libstdc++-v3/include/bits/stl_iterator.h > index d71a793e10d..560a10a7abe 100644 > --- a/libstdc++-v3/include/bits/stl_iterator.h > +++ b/libstdc++-v3/include/bits/stl_iterator.h > @@ -78,6 +78,10 @@ > # include <bits/stl_construct.h> > #endif > > +#if __glibcxx_tuple_like // >= C++23 > +# include <bits/utility.h> // for tuple_element_t > +#endif > + > namespace std _GLIBCXX_VISIBILITY(default) > { > _GLIBCXX_BEGIN_NAMESPACE_VERSION > @@ -95,10 +99,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > template<typename _Cat, typename _Limit, typename _Otherwise = _Cat> > using __clamp_iter_cat > = __conditional_t<derived_from<_Cat, _Limit>, _Limit, _Otherwise>; > - > - template<typename _Tp, typename _Up> > - concept __different_from > - = !same_as<remove_cvref_t<_Tp>, remove_cvref_t<_Up>>; > } > #endif > > @@ -2983,11 +2983,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > // of associative containers. > template<typename _InputIterator> > using __iter_key_t = remove_const_t< > +#if __glibcxx_tuple_like // >= C++23 > + tuple_element_t<0, typename > iterator_traits<_InputIterator>::value_type>>; > +#else > typename iterator_traits<_InputIterator>::value_type::first_type>; > +#endif > > template<typename _InputIterator> > using __iter_val_t > +#if __glibcxx_tuple_like // >= C++23 > + = tuple_element_t<1, typename > iterator_traits<_InputIterator>::value_type>; > +#else > = typename iterator_traits<_InputIterator>::value_type::second_type; > +#endif > > template<typename _T1, typename _T2> > struct pair; > diff --git a/libstdc++-v3/include/bits/stl_pair.h > b/libstdc++-v3/include/bits/stl_pair.h > index b81b479ad43..00ec53ebc33 100644 > --- a/libstdc++-v3/include/bits/stl_pair.h > +++ b/libstdc++-v3/include/bits/stl_pair.h > @@ -85,12 +85,70 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > /// @cond undocumented > > // Forward declarations. > + template<typename _T1, typename _T2> > + struct pair; > + > template<typename...> > class tuple; > > + // Declarations of std::array and its std::get overloads, so that > + // std::tuple_cat can use them if <tuple> is included before <array>. > + // We also declare the other std::get overloads here so that they're > + // visible to the P2165R4 tuple-like constructors of pair and tuple. > + template<typename _Tp, size_t _Nm> > + struct array; > + > template<size_t...> > struct _Index_tuple; > > + template<size_t _Int, class _Tp1, class _Tp2> > + constexpr typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type& > + get(pair<_Tp1, _Tp2>& __in) noexcept; > + > + template<size_t _Int, class _Tp1, class _Tp2> > + constexpr typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type&& > + get(pair<_Tp1, _Tp2>&& __in) noexcept; > + > + template<size_t _Int, class _Tp1, class _Tp2> > + constexpr const typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type& > + get(const pair<_Tp1, _Tp2>& __in) noexcept; > + > + template<size_t _Int, class _Tp1, class _Tp2> > + constexpr const typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type&& > + get(const pair<_Tp1, _Tp2>&& __in) noexcept; > + > + template<size_t __i, typename... _Elements> > + constexpr __tuple_element_t<__i, tuple<_Elements...>>& > + get(tuple<_Elements...>& __t) noexcept; > + > + template<size_t __i, typename... _Elements> > + constexpr const __tuple_element_t<__i, tuple<_Elements...>>& > + get(const tuple<_Elements...>& __t) noexcept; > + > + template<size_t __i, typename... _Elements> > + constexpr __tuple_element_t<__i, tuple<_Elements...>>&& > + get(tuple<_Elements...>&& __t) noexcept; > + > + template<size_t __i, typename... _Elements> > + constexpr const __tuple_element_t<__i, tuple<_Elements...>>&& > + get(const tuple<_Elements...>&& __t) noexcept; > + > + template<size_t _Int, typename _Tp, size_t _Nm> > + constexpr _Tp& > + get(array<_Tp, _Nm>&) noexcept; > + > + template<size_t _Int, typename _Tp, size_t _Nm> > + constexpr _Tp&& > + get(array<_Tp, _Nm>&&) noexcept; > + > + template<size_t _Int, typename _Tp, size_t _Nm> > + constexpr const _Tp& > + get(const array<_Tp, _Nm>&) noexcept; > + > + template<size_t _Int, typename _Tp, size_t _Nm> > + constexpr const _Tp&& > + get(const array<_Tp, _Nm>&&) noexcept; > + > #if ! __cpp_lib_concepts > // Concept utility functions, reused in conditionally-explicit > // constructors. > @@ -159,6 +217,46 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > #endif // lib concepts > #endif // C++11 > > +#if __glibcxx_tuple_like // >= C++23 > + template<typename _Tp> > + inline constexpr bool __is_tuple_v = false; > + > + template<typename... _Ts> > + inline constexpr bool __is_tuple_v<tuple<_Ts...>> = true; > + > + // TODO: Reuse __is_tuple_like from <type_traits>? > + template<typename _Tp> > + inline constexpr bool __is_tuple_like_v = false; > + > + template<typename... _Elements> > + inline constexpr bool __is_tuple_like_v<tuple<_Elements...>> = true; > + > + template<typename _T1, typename _T2> > + inline constexpr bool __is_tuple_like_v<pair<_T1, _T2>> = true; > + > + template<typename _Tp, size_t _Nm> > + inline constexpr bool __is_tuple_like_v<array<_Tp, _Nm>> = true; > + > + // __is_tuple_like_v<subrange> is defined in <bits/ranges_util.h>. > + > + template<typename _Tp> > + concept __tuple_like = __is_tuple_like_v<remove_cvref_t<_Tp>>; > + > + template<typename _Tp> > + concept __pair_like = __tuple_like<_Tp> && > tuple_size_v<remove_cvref_t<_Tp>> == 2; > + > + template<typename _Tp, typename _Tuple> > + concept __eligible_tuple_like > + = __detail::__different_from<_Tp, _Tuple> && __tuple_like<_Tp> > + && (tuple_size_v<remove_cvref_t<_Tp>> == tuple_size_v<_Tuple>) > + && !ranges::__detail::__is_subrange<remove_cvref_t<_Tp>>; > + > + template<typename _Tp, typename _Pair> > + concept __eligible_pair_like > + = __detail::__different_from<_Tp, _Pair> && __pair_like<_Tp> > + && !ranges::__detail::__is_subrange<remove_cvref_t<_Tp>>; > +#endif // C++23 > + > template<typename _U1, typename _U2> class __pair_base > { > #if __cplusplus >= 201103L && ! __cpp_lib_concepts > @@ -295,6 +393,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return false; > #endif > } > + > +#if __glibcxx_tuple_like // >= C++23 > + template<typename _UPair> > + static constexpr bool > + _S_constructible_from_pair_like() > + { > + return _S_constructible<decltype(std::get<0>(std::declval<_UPair>())), > + > decltype(std::get<1>(std::declval<_UPair>()))>(); > + } > + > + template<typename _UPair> > + static constexpr bool > + _S_convertible_from_pair_like() > + { > + return _S_convertible<decltype(std::get<0>(std::declval<_UPair>())), > + > decltype(std::get<1>(std::declval<_UPair>()))>(); > + } > + > + template<typename _UPair> > + static constexpr bool > + _S_dangles_from_pair_like() > + { > + return _S_dangles<decltype(std::get<0>(std::declval<_UPair>())), > + decltype(std::get<1>(std::declval<_UPair>()))>(); > + } > +#endif // C++23 > /// @endcond > > public: > @@ -393,6 +517,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > pair(const pair<_U1, _U2>&&) = delete; > #endif // C++23 > > +#if __glibcxx_tuple_like // >= C++23 > + template<__eligible_pair_like<pair> _UPair> > + requires (_S_constructible_from_pair_like<_UPair>()) > + && (!_S_dangles_from_pair_like<_UPair>()) > + constexpr explicit(!_S_convertible_from_pair_like<_UPair>()) > + pair(_UPair&& __p) > + : first(std::get<0>(std::forward<_UPair>(__p))), > + second(std::get<1>(std::forward<_UPair>(__p))) > + { } > + > + template<__eligible_pair_like<pair> _UPair> > + requires (_S_constructible_from_pair_like<_UPair>()) > + && (_S_dangles_from_pair_like<_UPair>()) > + constexpr explicit(!_S_convertible_from_pair_like<_UPair>()) > + pair(_UPair&&) = delete; > +#endif // C++23 > + > private: > /// @cond undocumented > template<typename _U1, typename _U2> > @@ -421,6 +562,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return is_nothrow_assignable_v<_T2&, _U2>; > return false; > } > + > +#if __glibcxx_tuple_like // >= C++23 > + template<typename _UPair> > + static constexpr bool > + _S_assignable_from_tuple_like() > + { > + return _S_assignable<decltype(std::get<0>(std::declval<_UPair>())), > + decltype(std::get<1>(std::declval<_UPair>()))>(); > + } > + > + template<typename _UPair> > + static constexpr bool > + _S_const_assignable_from_tuple_like() > + { > + return > _S_const_assignable<decltype(std::get<0>(std::declval<_UPair>())), > + > decltype(std::get<1>(std::declval<_UPair>()))>(); > + } > +#endif // C++23 > /// @endcond > > public: > @@ -516,6 +675,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > return *this; > } > #endif // C++23 > + > +#if __glibcxx_tuple_like // >= C++23 > + template<__eligible_pair_like<pair> _UPair> > + requires (_S_assignable_from_tuple_like<_UPair>()) > + constexpr pair& > + operator=(_UPair&& __p) > + { > + first = std::get<0>(std::forward<_UPair>(__p)); > + second = std::get<1>(std::forward<_UPair>(__p)); > + return *this; > + } > + > + template<__eligible_pair_like<pair> _UPair> > + requires (_S_const_assignable_from_tuple_like<_UPair>()) > + constexpr const pair& > + operator=(_UPair&& __p) const > + { > + first = std::get<0>(std::forward<_UPair>(__p)); > + second = std::get<1>(std::forward<_UPair>(__p)); > + return *this; > + } > +#endif // C++23 > + > #else // !__cpp_lib_concepts > // C++11/14/17 implementation using enable_if, partially constexpr. > > diff --git a/libstdc++-v3/include/bits/utility.h > b/libstdc++-v3/include/bits/utility.h > index d8a5fb960fe..2a741bf7000 100644 > --- a/libstdc++-v3/include/bits/utility.h > +++ b/libstdc++-v3/include/bits/utility.h > @@ -266,6 +266,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > #endif > #endif > > +#if __glibcxx_ranges > + namespace ranges::__detail > + { > + template<typename _Range> > + inline constexpr bool __is_subrange = false; > + } // namespace __detail > +#endif > + > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace > > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > index 8fb8a2877ee..502961eb269 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1780,6 +1780,14 @@ ftms = { > }; > }; > > +ftms = { > + name = tuple_like; > + values = { > + v = 202207; > + cxxmin = 23; > + }; > +}; > + > // Standard test specifications. > stds[97] = ">= 199711L"; > stds[03] = ">= 199711L"; > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > index 9ba99deeda6..511030bde47 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -2169,4 +2169,15 @@ > #endif /* !defined(__cpp_lib_generator) && defined(__glibcxx_want_generator) > */ > #undef __glibcxx_want_generator > > +// from version.def line 1774 > +#if !defined(__cpp_lib_tuple_like) > +# if (__cplusplus >= 202100L) > +# define __glibcxx_tuple_like 202207L > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_tuple_like) > +# define __cpp_lib_tuple_like 202207L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_tuple_like) && > defined(__glibcxx_want_tuple_like) */ > +#undef __glibcxx_want_tuple_like > + > #undef __glibcxx_want_all > diff --git a/libstdc++-v3/include/std/concepts > b/libstdc++-v3/include/std/concepts > index 66ed3714b25..4f3e059b051 100644 > --- a/libstdc++-v3/include/std/concepts > +++ b/libstdc++-v3/include/std/concepts > @@ -62,6 +62,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > concept same_as > = __detail::__same_as<_Tp, _Up> && __detail::__same_as<_Up, _Tp>; > > + namespace __detail > + { > + template<typename _Tp, typename _Up> > + concept __different_from > + = !same_as<remove_cvref_t<_Tp>, remove_cvref_t<_Up>>; > + } // namespace __detail > + > /// [concept.derived], concept derived_from > template<typename _Derived, typename _Base> > concept derived_from = __is_base_of(_Base, _Derived) > @@ -185,8 +192,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > template<typename _Tp, typename _Up> > concept __adl_swap > - = (__detail::__class_or_enum<remove_reference_t<_Tp>> > - || __detail::__class_or_enum<remove_reference_t<_Up>>) > + = (std::__detail::__class_or_enum<remove_reference_t<_Tp>> > + || std::__detail::__class_or_enum<remove_reference_t<_Up>>) > && requires(_Tp&& __t, _Up&& __u) { > swap(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u)); > }; > diff --git a/libstdc++-v3/include/std/map b/libstdc++-v3/include/std/map > index dcfd222d173..4a96e59a5bc 100644 > --- a/libstdc++-v3/include/std/map > +++ b/libstdc++-v3/include/std/map > @@ -74,6 +74,7 @@ > #define __glibcxx_want_map_try_emplace > #define __glibcxx_want_node_extract > #define __glibcxx_want_nonmember_container_access > +#define __glibcxx_want_tuple_like > #include <bits/version.h> > > #if __cplusplus >= 201703L > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > index f2413badd9c..7d739852677 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -2389,11 +2389,7 @@ namespace views::__adaptor > inline constexpr bool __is_basic_string_view<basic_string_view<_CharT, > _Traits>> > = true; > > - template<typename _Range> > - inline constexpr bool __is_subrange = false; > - > - template<typename _Iter, typename _Sent, subrange_kind _Kind> > - inline constexpr bool __is_subrange<subrange<_Iter, _Sent, _Kind>> = > true; > + using ranges::__detail::__is_subrange; > > template<typename _Range> > inline constexpr bool __is_iota_view = false; > @@ -4166,6 +4162,10 @@ namespace views::__adaptor > > namespace __detail > { > +#if __cpp_lib_tuple_like // >= C++23 > + template<typename _Tp, size_t _Nm> > + concept __has_tuple_element = __tuple_like<_Tp> && _Nm < > tuple_size_v<_Tp>; > +#else > template<typename _Tp, size_t _Nm> > concept __has_tuple_element = requires(_Tp __t) > { > @@ -4175,6 +4175,7 @@ namespace views::__adaptor > { std::get<_Nm>(__t) } > -> convertible_to<const tuple_element_t<_Nm, _Tp>&>; > }; > +#endif > > template<typename _Tp, size_t _Nm> > concept __returnable_element > @@ -4559,23 +4560,12 @@ namespace views::__adaptor > || (!(bidirectional_range<_Rs> && ...) && (common_range<_Rs> && ...)) > || ((random_access_range<_Rs> && ...) && (sized_range<_Rs> && ...)); > > - template<typename... _Ts> > - struct __tuple_or_pair > - { using type = std::tuple<_Ts...>; }; > - > - template<typename _Tp, typename _Up> > - struct __tuple_or_pair<_Tp, _Up> > - { using type = pair<_Tp, _Up>; }; > - > - template<typename... _Ts> > - using __tuple_or_pair_t = typename __tuple_or_pair<_Ts...>::type; > - > template<typename _Fp, typename _Tuple> > constexpr auto > __tuple_transform(_Fp&& __f, _Tuple&& __tuple) > { > return std::apply([&]<typename... _Ts>(_Ts&&... __elts) { > - return __tuple_or_pair_t<invoke_result_t<_Fp&, _Ts>...> > + return tuple<invoke_result_t<_Fp&, _Ts>...> > (std::__invoke(__f, std::forward<_Ts>(__elts))...); > }, std::forward<_Tuple>(__tuple)); > } > @@ -4696,7 +4686,7 @@ namespace views::__adaptor > #ifdef __clang__ // LLVM-61763 workaround > public: > #endif > - __detail::__tuple_or_pair_t<iterator_t<__detail::__maybe_const_t<_Const, > _Vs>>...> _M_current; > + tuple<iterator_t<__detail::__maybe_const_t<_Const, _Vs>>...> _M_current; > > constexpr explicit > _Iterator(decltype(_M_current) __current) > @@ -4728,7 +4718,7 @@ namespace views::__adaptor > // iterator_category defined in __zip_view_iter_cat > using iterator_concept = decltype(_S_iter_concept()); > using value_type > - = > __detail::__tuple_or_pair_t<range_value_t<__detail::__maybe_const_t<_Const, > _Vs>>...>; > + = tuple<range_value_t<__detail::__maybe_const_t<_Const, _Vs>>...>; > using difference_type > = common_type_t<range_difference_t<__detail::__maybe_const_t<_Const, > _Vs>>...>; > > @@ -4900,7 +4890,7 @@ namespace views::__adaptor > template<bool _Const> > class zip_view<_Vs...>::_Sentinel > { > - __detail::__tuple_or_pair_t<sentinel_t<__detail::__maybe_const_t<_Const, > _Vs>>...> _M_end; > + tuple<sentinel_t<__detail::__maybe_const_t<_Const, _Vs>>...> _M_end; > > constexpr explicit > _Sentinel(decltype(_M_end) __end) > @@ -8325,8 +8315,7 @@ namespace views::__adaptor > && __detail::__cartesian_product_is_common<_First, _Vs...>) > { > auto __its = [this]<size_t... _Is>(index_sequence<_Is...>) { > - using _Ret = __detail::__tuple_or_pair_t<iterator_t<_First>, > - iterator_t<_Vs>...>; > + using _Ret = tuple<iterator_t<_First>, iterator_t<_Vs>...>; > bool __empty_tail = (ranges::empty(std::get<1 + _Is>(_M_bases)) || ...); > auto& __first = std::get<0>(_M_bases); > return _Ret{(__empty_tail > @@ -8342,8 +8331,7 @@ namespace views::__adaptor > end() const requires __detail::__cartesian_product_is_common<const > _First, const _Vs...> > { > auto __its = [this]<size_t... _Is>(index_sequence<_Is...>) { > - using _Ret = __detail::__tuple_or_pair_t<iterator_t<const _First>, > - iterator_t<const _Vs>...>; > + using _Ret = tuple<iterator_t<const _First>, iterator_t<const _Vs>...>; > bool __empty_tail = (ranges::empty(std::get<1 + _Is>(_M_bases)) || ...); > auto& __first = std::get<0>(_M_bases); > return _Ret{(__empty_tail > @@ -8416,8 +8404,8 @@ namespace views::__adaptor > { > using _Parent = __maybe_const_t<_Const, cartesian_product_view>; > _Parent* _M_parent = nullptr; > - __detail::__tuple_or_pair_t<iterator_t<__maybe_const_t<_Const, _First>>, > - iterator_t<__maybe_const_t<_Const, _Vs>>...> > _M_current; > + tuple<iterator_t<__maybe_const_t<_Const, _First>>, > + iterator_t<__maybe_const_t<_Const, _Vs>>...> _M_current; > > constexpr > _Iterator(_Parent& __parent, decltype(_M_current) __current) > @@ -8444,11 +8432,11 @@ namespace views::__adaptor > using iterator_category = input_iterator_tag; > using iterator_concept = decltype(_S_iter_concept()); > using value_type > - = __detail::__tuple_or_pair_t<range_value_t<__maybe_const_t<_Const, > _First>>, > - range_value_t<__maybe_const_t<_Const, > _Vs>>...>; > + = tuple<range_value_t<__maybe_const_t<_Const, _First>>, > + range_value_t<__maybe_const_t<_Const, _Vs>>...>; > using reference > - = > __detail::__tuple_or_pair_t<range_reference_t<__maybe_const_t<_Const, > _First>>, > - range_reference_t<__maybe_const_t<_Const, > _Vs>>...>; > + = tuple<range_reference_t<__maybe_const_t<_Const, _First>>, > + range_reference_t<__maybe_const_t<_Const, _Vs>>...>; > using difference_type = > decltype(cartesian_product_view::_S_difference_type()); > > _Iterator() = default; > diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple > index be92f1eb973..ba364d6a4f8 100644 > --- a/libstdc++-v3/include/std/tuple > +++ b/libstdc++-v3/include/std/tuple > @@ -50,6 +50,7 @@ > #define __glibcxx_want_apply > #define __glibcxx_want_make_from_tuple > #define __glibcxx_want_ranges_zip > +#define __glibcxx_want_tuple_like > #include <bits/version.h> > > namespace std _GLIBCXX_VISIBILITY(default) > @@ -246,6 +247,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > _Head _M_head_impl; > }; > > +#if __cpp_lib_tuple_like // >= C++23 > + struct __tuple_like_tag_t { explicit __tuple_like_tag_t() = default; }; > + > + // These forward declarations are used by the operator<=> overload for > + // tuple-like types. > + template<typename _Cat, typename _Tp, typename _Up> > + constexpr _Cat > + __tuple_cmp(const _Tp&, const _Up&, index_sequence<>); > + > + template<typename _Cat, typename _Tp, typename _Up, > + size_t _Idx0, size_t... _Idxs> > + constexpr _Cat > + __tuple_cmp(const _Tp& __t, const _Up& __u, > + index_sequence<_Idx0, _Idxs...>); > +#endif // C++23 > + > /** > * Contains the actual implementation of the @c tuple template, stored > * as a recursive inheritance hierarchy from the first element (most > @@ -342,6 +359,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > { } > #endif // C++23 > > +#if __cpp_lib_tuple_like // >= C++23 > + template<typename _UTuple, size_t... _Is> > + constexpr > + _Tuple_impl(__tuple_like_tag_t, _UTuple&& __u, index_sequence<_Is...>) > + : _Tuple_impl(std::get<_Is>(std::forward<_UTuple>(__u))...) > + { } > +#endif // C++23 > + > template<typename _Alloc> > _GLIBCXX20_CONSTEXPR > _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a) > @@ -428,6 +453,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > { } > #endif // C++23 > > +#if __cpp_lib_tuple_like // >= C++23 > + template<typename _Alloc, typename _UTuple, size_t... _Is> > + constexpr > + _Tuple_impl(__tuple_like_tag_t, allocator_arg_t __tag, const _Alloc& > __a, > + _UTuple&& __u, index_sequence<_Is...>) > + : _Tuple_impl(__tag, __a, std::get<_Is>(std::forward<_UTuple>(__u))...) > + { } > +#endif // C++23 > + > template<typename... _UElements> > _GLIBCXX20_CONSTEXPR > void > @@ -470,6 +504,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > } > #endif // C++23 > > +#if __cpp_lib_tuple_like // >= C++23 > + template<typename _UTuple> > + constexpr void > + _M_assign(__tuple_like_tag_t __tag, _UTuple&& __u) > + { > + _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); > + _M_tail(*this)._M_assign(__tag, std::forward<_UTuple>(__u)); > + } > + > + template<typename _UTuple> > + constexpr void > + _M_assign(__tuple_like_tag_t __tag, _UTuple&& __u) const > + { > + _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); > + _M_tail(*this)._M_assign(__tag, std::forward<_UTuple>(__u)); > + } > +#endif // C++23 > + > protected: > _GLIBCXX20_CONSTEXPR > void > @@ -563,6 +615,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > { } > #endif // C++23 > > +#if __cpp_lib_tuple_like // >= C++23 > + template<typename _UTuple> > + constexpr > + _Tuple_impl(__tuple_like_tag_t, _UTuple&& __u, index_sequence<0>) > + : _Tuple_impl(std::get<0>(std::forward<_UTuple>(__u))) > + { } > +#endif // C++23 > + > template<typename _Alloc> > _GLIBCXX20_CONSTEXPR > _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a) > @@ -633,6 +693,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > { } > #endif // C++23 > > +#if __cpp_lib_tuple_like // >= C++23 > + template<typename _Alloc, typename _UTuple> > + constexpr > + _Tuple_impl(__tuple_like_tag_t, allocator_arg_t __tag, const _Alloc& > __a, > + _UTuple&& __u, index_sequence<0>) > + : _Tuple_impl(__tag, __a, std::get<0>(std::forward<_UTuple>(__u))) > + { } > +#endif // C++23 > + > template<typename _UHead> > _GLIBCXX20_CONSTEXPR > void > @@ -667,6 +736,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > } > #endif // C++23 > > +#if __cpp_lib_tuple_like // >= C++23 > + template<typename _UTuple> > + constexpr void > + _M_assign(__tuple_like_tag_t, _UTuple&& __u) > + { _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); } > + > + template<typename _UTuple> > + constexpr void > + _M_assign(__tuple_like_tag_t, _UTuple&& __u) const > + { _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); } > +#endif // C++23 > + > protected: > _GLIBCXX20_CONSTEXPR > void > @@ -846,6 +927,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > #endif > } > > +#if __cpp_lib_tuple_like // >= C++23 > + template<typename _UTuple> > + static consteval bool > + __dangles_from_tuple_like() > + { > + return []<size_t... _Is>(index_sequence<_Is...>) { > + return > __dangles<decltype(std::get<_Is>(std::declval<_UTuple>()))...>(); > + }(index_sequence_for<_Elements...>{}); > + } > + > + template<typename _UTuple> > + static consteval bool > + __constructible_from_tuple_like() > + { > + return []<size_t... _Is>(index_sequence<_Is...>) { > + return > __constructible<decltype(std::get<_Is>(std::declval<_UTuple>()))...>(); > + }(index_sequence_for<_Elements...>{}); > + } > + > + template<typename _UTuple> > + static consteval bool > + __convertible_from_tuple_like() > + { > + return []<size_t... _Is>(index_sequence<_Is...>) { > + return > __convertible<decltype(std::get<_Is>(std::declval<_UTuple>()))...>(); > + }(index_sequence_for<_Elements...>{}); > + } > +#endif // C++23 > + > public: > constexpr > explicit(!(__is_implicitly_default_constructible_v<_Elements> && ...)) > @@ -1016,10 +1126,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > tuple(const pair<_U1, _U2>&&) = delete; > #endif // C++23 > > -#if 0 && __cpp_lib_tuple_like // >= C++23 > - template<__tuple_like _UTuple> > - constexpr explicit(...) > - tuple(_UTuple&& __u); > +#if __cpp_lib_tuple_like // >= C++23 > + template<__eligible_tuple_like<tuple> _UTuple> > + requires (__constructible_from_tuple_like<_UTuple>()) > + && (!__use_other_ctor<_UTuple>()) > + && (!__dangles_from_tuple_like<_UTuple>()) > + constexpr explicit(!__convertible_from_tuple_like<_UTuple>()) > + tuple(_UTuple&& __u) > + : _Inherited(__tuple_like_tag_t{}, > + std::forward<_UTuple>(__u), > + index_sequence_for<_Elements...>{}) > + { } > + > + template<__eligible_tuple_like<tuple> _UTuple> > + requires (__constructible_from_tuple_like<_UTuple>()) > + && (!__use_other_ctor<_UTuple>()) > + && (__dangles_from_tuple_like<_UTuple>()) > + tuple(_UTuple&&) = delete; > #endif // C++23 > > // Allocator-extended constructors. > @@ -1202,10 +1325,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete; > #endif // C++23 > > -#if 0 && __cpp_lib_tuple_like // >= C++23 > - template<typename _Alloc, __tuple_like _UTuple> > - constexpr explicit(...) > - tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& __u); > +#if __cpp_lib_tuple_like // >= C++23 > + template<typename _Alloc, __eligible_tuple_like<tuple> _UTuple> > + requires (__constructible_from_tuple_like<_UTuple>()) > + && (!__use_other_ctor<_UTuple>()) > + && (!__dangles_from_tuple_like<_UTuple>()) > + constexpr explicit(!__convertible_from_tuple_like<_UTuple>()) > + tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& __u) > + : _Inherited(__tuple_like_tag_t{}, > + __tag, __a, std::forward<_UTuple>(__u), > + index_sequence_for<_Elements...>{}) > + { } > + > + template<typename _Alloc, __eligible_tuple_like<tuple> _UTuple> > + requires (__constructible_from_tuple_like<_UTuple>()) > + && (!__use_other_ctor<_UTuple>()) > + && (__dangles_from_tuple_like<_UTuple>()) > + tuple(allocator_arg_t, const _Alloc&, _UTuple&&) = delete; > #endif // C++23 > > #else // !(concepts && conditional_explicit) > @@ -1539,6 +1675,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > } > #endif // C++23 > > +#if __cpp_lib_tuple_like // >= C++23 > + template<typename _UTuple> > + static consteval bool > + __assignable_from_tuple_like() > + { > + return []<size_t... _Is>(index_sequence<_Is...>) { > + return > __assignable<decltype(std::get<_Is>(std::declval<_UTuple>()))...>(); > + }(index_sequence_for<_Elements...>{}); > + } > + > + template<typename _UTuple> > + static consteval bool > + __const_assignable_from_tuple_like() > + { > + return []<size_t... _Is>(index_sequence<_Is...>) { > + return > __const_assignable<decltype(std::get<_Is>(std::declval<_UTuple>()))...>(); > + }(index_sequence_for<_Elements...>{}); > + } > +#endif // C++23 > + > public: > > tuple& operator=(const tuple& __u) = delete; > @@ -1661,14 +1817,59 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > } > #endif // C++23 > > -#if 0 && __cpp_lib_tuple_like // >= C++23 > - template<__tuple_like _UTuple> > +#if __cpp_lib_tuple_like // >= C++23 > + template<__eligible_tuple_like<tuple> _UTuple> > + requires (__assignable_from_tuple_like<_UTuple>()) > constexpr tuple& > - operator=(_UTuple&& __u); > + operator=(_UTuple&& __u) > + { > + this->_M_assign(__tuple_like_tag_t{}, std::forward<_UTuple>(__u)); > + return *this; > + } > + > + template<__eligible_tuple_like<tuple> _UTuple> > + requires (__const_assignable_from_tuple_like<_UTuple>()) > + constexpr const tuple& > + operator=(_UTuple&& __u) const > + { > + this->_M_assign(__tuple_like_tag_t{}, std::forward<_UTuple>(__u)); > + return *this; > + } > > template<__tuple_like _UTuple> > - constexpr tuple& > - operator=(_UTuple&& __u) const; > + requires (!__is_tuple_v<_UTuple>) > + friend constexpr bool > + operator==(const tuple& __t, const _UTuple& __u) > + { > + static_assert(sizeof...(_Elements) == tuple_size_v<_UTuple>, > + "tuple objects can only be compared if they have equal sizes."); > + return [&]<size_t... _Is>(index_sequence<_Is...>) { > + return (bool(std::get<_Is>(__t) == std::get<_Is>(__u)) > + && ...); > + }(index_sequence_for<_Elements...>{}); > + } > + > + template<__tuple_like _UTuple, > + typename = make_index_sequence<tuple_size_v<_UTuple>>> > + struct __tuple_like_common_comparison_category; > + > + template<__tuple_like _UTuple, size_t... _Is> > + requires requires > + { typename void_t<__detail::__synth3way_t<_Elements, > tuple_element_t<_Is, _UTuple>>...>; } > + struct __tuple_like_common_comparison_category<_UTuple, > index_sequence<_Is...>> > + { > + using type = common_comparison_category_t > + <__detail::__synth3way_t<_Elements, tuple_element_t<_Is, > _UTuple>>...>; > + }; > + > + template<__tuple_like _UTuple> > + requires (!__is_tuple_v<_UTuple>) > + friend constexpr typename > __tuple_like_common_comparison_category<_UTuple>::type > + operator<=>(const tuple& __t, const _UTuple& __u) > + { > + using _Cat = typename > __tuple_like_common_comparison_category<_UTuple>::type; > + return std::__tuple_cmp<_Cat>(__t, __u, > index_sequence_for<_Elements...>()); > + } > #endif // C++23 > > #else // ! (concepts && consteval) > @@ -2433,27 +2634,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > forward_as_tuple(_Elements&&... __args) noexcept > { return tuple<_Elements&&...>(std::forward<_Elements>(__args)...); } > > - // Declarations of std::array and its std::get overloads, so that > - // std::tuple_cat can use them if <tuple> is included before <array>. > - > - template<typename _Tp, size_t _Nm> struct array; > - > - template<size_t _Int, typename _Tp, size_t _Nm> > - constexpr _Tp& > - get(array<_Tp, _Nm>&) noexcept; > - > - template<size_t _Int, typename _Tp, size_t _Nm> > - constexpr _Tp&& > - get(array<_Tp, _Nm>&&) noexcept; > - > - template<size_t _Int, typename _Tp, size_t _Nm> > - constexpr const _Tp& > - get(const array<_Tp, _Nm>&) noexcept; > - > - template<size_t _Int, typename _Tp, size_t _Nm> > - constexpr const _Tp&& > - get(const array<_Tp, _Nm>&&) noexcept; > - > /// @cond undocumented > template<size_t, typename, typename, size_t> > struct __make_tuple_impl; > @@ -2569,8 +2749,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > /// @endcond > > /// Create a `tuple` containing all elements from multiple tuple-like > objects > +#if __cpp_lib_tuple_like // >= C++23 > + template<__tuple_like... _Tpls> > +#else > template<typename... _Tpls, typename = typename > enable_if<__and_<__is_tuple_like<_Tpls>...>::value>::type> > +#endif > constexpr auto > tuple_cat(_Tpls&&... __tpls) > -> typename __tuple_cat_result<_Tpls...>::__type > @@ -2722,7 +2906,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > std::get<_Idx>(std::forward<_Tuple>(__t))...); > } > > +#if __cpp_lib_tuple_like // >= C++23 > + template <typename _Fn, __tuple_like _Tuple> > +#else > template <typename _Fn, typename _Tuple> > +#endif > constexpr decltype(auto) > apply(_Fn&& __f, _Tuple&& __t) > noexcept(__unpack_std_tuple<is_nothrow_invocable, _Fn, _Tuple>) > @@ -2741,7 +2929,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __make_from_tuple_impl(_Tuple&& __t, index_sequence<_Idx...>) > { return _Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...); } > > +#if __cpp_lib_tuple_like // >= C++23 > + template <typename _Tp, __tuple_like _Tuple> > +#else > template <typename _Tp, typename _Tuple> > +#endif > constexpr _Tp > make_from_tuple(_Tuple&& __t) > noexcept(__unpack_std_tuple<is_nothrow_constructible, _Tp, _Tuple>) > @@ -2759,17 +2951,60 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > } > #endif > > -#if __cpp_lib_ranges_zip // >= C++23 > - template<typename... _TTypes, typename... _UTypes, > +#if __cpp_lib_tuple_like // >= C++23 > + template<__tuple_like _TTuple, __tuple_like _UTuple, > + template<typename> class _TQual, template<typename> class _UQual, > + typename = make_index_sequence<tuple_size_v<_TTuple>>> > + struct __tuple_like_common_reference; > + > + template<__tuple_like _TTuple, __tuple_like _UTuple, > + template<typename> class _TQual, template<typename> class _UQual, > + size_t... _Is> > + requires requires > + { typename tuple<common_reference_t<_TQual<tuple_element_t<_Is, > _TTuple>>, > + _UQual<tuple_element_t<_Is, > _UTuple>>>...>; } > + struct __tuple_like_common_reference<_TTuple, _UTuple, _TQual, _UQual, > index_sequence<_Is...>> > + { > + using type = tuple<common_reference_t<_TQual<tuple_element_t<_Is, > _TTuple>>, > + _UQual<tuple_element_t<_Is, > _UTuple>>>...>; > + }; > + > + template<__tuple_like _TTuple, __tuple_like _UTuple, > 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>...>; }; > + requires (__is_tuple_v<_TTuple> || __is_tuple_v<_UTuple>) > + && is_same_v<_TTuple, decay_t<_TTuple>> > + && is_same_v<_UTuple, decay_t<_UTuple>> > + && (tuple_size_v<_TTuple> == tuple_size_v<_UTuple>) > + && requires { typename __tuple_like_common_reference<_TTuple, _UTuple, > _TQual, _UQual>::type; } > + struct basic_common_reference<_TTuple, _UTuple, _TQual, _UQual> > + { > + using type = typename __tuple_like_common_reference<_TTuple, _UTuple, > _TQual, _UQual>::type; > + }; > + > + template<__tuple_like _TTuple, __tuple_like _UTuple, > + typename = make_index_sequence<tuple_size_v<_TTuple>>> > + struct __tuple_like_common_type; > + > + template<__tuple_like _TTuple, __tuple_like _UTuple, size_t... _Is> > + requires requires > + { typename tuple<common_type_t<tuple_element_t<_Is, _TTuple>, > + tuple_element_t<_Is, _UTuple>>...>; } > + struct __tuple_like_common_type<_TTuple, _UTuple, index_sequence<_Is...>> > + { > + using type = tuple<common_type_t<tuple_element_t<_Is, _TTuple>, > + tuple_element_t<_Is, _UTuple>>...>; > + }; > + > + template<__tuple_like _TTuple, __tuple_like _UTuple> > + requires (__is_tuple_v<_TTuple> || __is_tuple_v<_UTuple>) > + && is_same_v<_TTuple, decay_t<_TTuple>> > + && is_same_v<_UTuple, decay_t<_UTuple>> > + && (tuple_size_v<_TTuple> == tuple_size_v<_UTuple>) > + && requires { typename __tuple_like_common_type<_TTuple, > _UTuple>::type; } > + struct common_type<_TTuple, _UTuple> > + { > + using type = typename __tuple_like_common_type<_TTuple, _UTuple>::type; > + }; > #endif // C++23 > > /// @} > diff --git a/libstdc++-v3/include/std/unordered_map > b/libstdc++-v3/include/std/unordered_map > index efad0cef584..ea6129d6494 100644 > --- a/libstdc++-v3/include/std/unordered_map > +++ b/libstdc++-v3/include/std/unordered_map > @@ -51,6 +51,7 @@ > #define __glibcxx_want_node_extract > #define __glibcxx_want_nonmember_container_access > #define __glibcxx_want_unordered_map_try_emplace > +#define __glibcxx_want_tuple_like > #include <bits/version.h> > > #if __cplusplus >= 201703L > diff --git a/libstdc++-v3/include/std/utility > b/libstdc++-v3/include/std/utility > index f113d572e59..212513f6f48 100644 > --- a/libstdc++-v3/include/std/utility > +++ b/libstdc++-v3/include/std/utility > @@ -92,6 +92,7 @@ > #define __glibcxx_want_tuple_element_t > #define __glibcxx_want_tuples_by_type > #define __glibcxx_want_unreachable > +#define __glibcxx_want_tuple_like > #include <bits/version.h> > > namespace std _GLIBCXX_VISIBILITY(default) > diff --git a/libstdc++-v3/testsuite/20_util/pair/p2165r4.cc > b/libstdc++-v3/testsuite/20_util/pair/p2165r4.cc > new file mode 100644 > index 00000000000..ef06df1c53f > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/pair/p2165r4.cc > @@ -0,0 +1,173 @@ > +// Verify P2165R4 enhancements to std::pair. > +// { dg-do run { target c++23 } } > + > +#include <array> > +#include <tuple> > +#include <utility> > +#include <testsuite_hooks.h> > + > +using std::array; > +using std::pair; > +using std::tuple; > + > +struct A { }; > + > +template<template<typename> class pair_like_t> > +constexpr bool > +test01() > +{ > + struct B { > + int m; > + constexpr B(A&) : m(0) { } > + constexpr B(A&&) : m(1) { } > + constexpr B(const A&) : m(2) { } > + constexpr B(const A&&) : m(3) { } > + }; > + > + // template<pair-like UPair> > + // constexpr explicit(false) pair(UPair&&); > + > + pair_like_t<A> pair_like; > + > + [&] { > + pair<B, B> p2b = pair_like; > + VERIFY( p2b.first.m == 0 && p2b.second.m == 0 ); > + }(); > + [&] { > + pair<B, B> p2b = std::move(pair_like); > + VERIFY( p2b.first.m == 1 && p2b.second.m == 1 ); > + }(); > + [&] { > + pair<B, B> p2b = std::as_const(pair_like); > + VERIFY( p2b.first.m == 2 && p2b.second.m == 2 ); > + }(); > + [&] { > + pair<B, B> p2b = std::move(std::as_const(pair_like)); > + VERIFY( p2b.first.m == 3 && p2b.second.m == 3 ); > + }(); > + > + // Verify dangling checks. > + static_assert( !std::is_constructible_v<pair<const int&, int>, > pair_like_t<long>> ); > + static_assert( !std::is_constructible_v<pair<int, const int&>, > pair_like_t<long>> ); > + > + return true; > +} > + > +template<template<typename> class pair_like_t> > +constexpr bool > +test02() > +{ > + struct B { > + int m; > + constexpr explicit B(A&) : m(0) { } > + constexpr explicit B(A&&) : m(1) { } > + constexpr explicit B(const A&) : m(2) { } > + constexpr explicit B(const A&&) : m(3) { } > + }; > + > + // template<pair-like UPair> > + // constexpr explicit(true) pair(UPair&&); > + > + static_assert( !std::is_convertible_v<pair_like_t<A>, pair<B, B>> ); > + > + pair_like_t<A> pair_like; > + > + [&] { > + pair<B, B> p2b{pair_like}; > + VERIFY( p2b.first.m == 0 && p2b.second.m == 0 ); > + }(); > + [&] { > + pair<B, B> p2b{std::move(pair_like)}; > + VERIFY( p2b.first.m == 1 && p2b.second.m == 1 ); > + }(); > + [&] { > + pair<B, B> p2b{std::as_const(pair_like)}; > + VERIFY( p2b.first.m == 2 && p2b.second.m == 2 ); > + }(); > + [&] { > + pair<B, B> p2b{std::move(std::as_const(pair_like))}; > + VERIFY( p2b.first.m == 3 && p2b.second.m == 3 ); > + }(); > + > + return true; > +} > + > +template<template<typename> class pair_like_t> > +constexpr bool > +test03() > +{ > + struct B { > + int m; > + constexpr B& operator=(A&) { m = 0; return *this; } > + constexpr B& operator=(A&&) { m = 1; return *this; } > + constexpr B& operator=(const A&) { m = 2; return *this; } > + constexpr B& operator=(const A&&) { m = 3; return *this; } > + }; > + > + // template<pair-like UPair> > + // constexpr pair& operator=(UPair&&); > + > + pair_like_t<A> pair_like; > + > + pair<B, B> p2b; > + p2b = pair_like; > + VERIFY( p2b.first.m == 0 && p2b.second.m == 0 ); > + p2b = std::move(pair_like); > + VERIFY( p2b.first.m == 1 && p2b.second.m == 1 ); > + p2b = std::as_const(pair_like); > + VERIFY( p2b.first.m == 2 && p2b.second.m == 2 ); > + p2b = std::move(std::as_const(pair_like)); > + VERIFY( p2b.first.m == 3 && p2b.second.m == 3 ); > + > + return true; > +} > + > +template<template<typename> class pair_like_t> > +constexpr bool > +test04() > +{ > + struct B { > + mutable int m; > + constexpr const B& operator=(A&) const { m = 0; return *this; } > + constexpr const B& operator=(A&&) const { m = 1; return *this; } > + constexpr const B& operator=(const A&) const { m = 2; return *this; } > + constexpr const B& operator=(const A&&) const { m = 3; return *this; } > + }; > + > + // template<pair-like UPair> > + // constexpr const pair& operator=(UPair&&) const; > + > + pair_like_t<A> pair_like; > + > + const pair<B, B> p2b; > + p2b = pair_like; > + VERIFY( p2b.first.m == 0 && p2b.second.m == 0 ); > + p2b = std::move(pair_like); > + VERIFY( p2b.first.m == 1 && p2b.second.m == 1 ); > + p2b = std::as_const(pair_like); > + VERIFY( p2b.first.m == 2 && p2b.second.m == 2 ); > + p2b = std::move(std::as_const(pair_like)); > + VERIFY( p2b.first.m == 3 && p2b.second.m == 3 ); > + > + return true; > +} > + > +template<typename T> > +using pair_like_array = array<T, 2>; > + > +template<typename T> > +using pair_like_tuple = tuple<T, T>; > + > +int > +main() > +{ > + static_assert( test01<pair_like_array>() ); > + static_assert( test02<pair_like_array>() ); > + static_assert( test03<pair_like_array>() ); > + static_assert( test04<pair_like_array>() ); > + > + static_assert( test01<pair_like_tuple>() ); > + static_assert( test02<pair_like_tuple>() ); > + static_assert( test03<pair_like_tuple>() ); > + static_assert( test04<pair_like_tuple>() ); > +} > diff --git a/libstdc++-v3/testsuite/20_util/tuple/p2165r4.cc > b/libstdc++-v3/testsuite/20_util/tuple/p2165r4.cc > new file mode 100644 > index 00000000000..e2437c469b6 > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/tuple/p2165r4.cc > @@ -0,0 +1,335 @@ > +// Verify P2165R4 enhancements to std::tuple. > +// { dg-do run { target c++23 } } > + > +#include <array> > +#include <tuple> > +#include <utility> > +#include <memory> > +#include <testsuite_hooks.h> > + > +using std::array; > +using std::pair; > +using std::tuple; > +using std::allocator; > +using std::allocator_arg_t; > +using std::allocator_arg; > + > +namespace alloc { > + struct B01; > + struct B02; > +} > + > +template<> struct std::uses_allocator<alloc::B01, allocator<int>> : > std::true_type { }; > +template<> struct std::uses_allocator<alloc::B02, allocator<int>> : > std::true_type { }; > + > +struct A { }; > + > +template<template<typename> class tuple_like_t> > +constexpr bool > +test01() > +{ > + struct B { > + int m; > + constexpr B(A&) : m(0) { } > + constexpr B(A&&) : m(1) { } > + constexpr B(const A&) : m(2) { } > + constexpr B(const A&&) : m(3) { } > + }; > + > + // template<tuple-like UTuple> > + // constexpr explicit(false) tuple(UTuple&&); > + > + tuple_like_t<A> tuple_like; > + > + [&] { > + tuple<B, B, B> t3b = tuple_like; > + VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && > std::get<2>(t3b).m == 0 ); > + }(); > + [&] { > + tuple<B, B, B> t3b = std::move(tuple_like); > + VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && > std::get<2>(t3b).m == 1 ); > + }(); > + [&] { > + tuple<B, B, B> t3b = std::as_const(tuple_like); > + VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && > std::get<2>(t3b).m == 2 ); > + }(); > + [&] { > + tuple<B, B, B> t3b = std::move(std::as_const(tuple_like)); > + VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && > std::get<2>(t3b).m == 3 ); > + }(); > + > + // Verify dangling checks. > + static_assert( !std::is_constructible_v<tuple<const int&, int, int>, > tuple_like_t<long>> ); > + static_assert( !std::is_constructible_v<tuple<int, const int&, int>, > tuple_like_t<long>> ); > + static_assert( !std::is_constructible_v<tuple<int, int, const int&>, > tuple_like_t<long>> ); > + > + return true; > +} > + > +namespace alloc > +{ > + struct B01 { > + int m; > + B01(A&); > + B01(A&&); > + B01(const A&); > + B01(const A&&); > + constexpr B01(allocator_arg_t, allocator<int>, A&) : m(0) { } > + constexpr B01(allocator_arg_t, allocator<int>, A&&) : m(1) { } > + constexpr B01(allocator_arg_t, allocator<int>, const A&) : m(2) { } > + constexpr B01(allocator_arg_t, allocator<int>, const A&&) : m(3) { } > + }; > + > + template<template<typename> class tuple_like_t> > + constexpr bool > + test01() > + { > + using B = B01; > + > + // template<tuple-like UTuple> > + // constexpr explicit(false) tuple(allocator_arg_t, const Alloc&, > UTuple&&); > + > + tuple_like_t<A> tuple_like; > + > + [&] { > + tuple<B, B, B> t3b = {allocator_arg, allocator<int>{}, tuple_like}; > + VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && > std::get<2>(t3b).m == 0 ); > + }(); > + [&] { > + tuple<B, B, B> t3b = {allocator_arg, allocator<int>{}, > std::move(tuple_like)}; > + VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && > std::get<2>(t3b).m == 1 ); > + }(); > + [&] { > + tuple<B, B, B> t3b = {allocator_arg, allocator<int>{}, > std::as_const(tuple_like)}; > + VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && > std::get<2>(t3b).m == 2 ); > + }(); > + [&] { > + tuple<B, B, B> t3b = {allocator_arg, allocator<int>{}, > std::move(std::as_const(tuple_like))}; > + VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && > std::get<2>(t3b).m == 3 ); > + }(); > + > + // Verify dangling checks. > + static_assert( !std::is_constructible_v<tuple<const int&, int, int>, > + allocator_arg_t, allocator<int>, > + tuple_like_t<long>> ); > + static_assert( !std::is_constructible_v<tuple<int, const int&, int>, > + allocator_arg_t, allocator<int>, > + tuple_like_t<long>> ); > + static_assert( !std::is_constructible_v<tuple<int, int, const int&>, > + allocator_arg_t, allocator<int>, > + tuple_like_t<long>> ); > + > + return true; > + } > +} > + > +template<template<typename> class tuple_like_t> > +constexpr bool > +test02() > +{ > + struct B { > + int m; > + constexpr explicit B(A&) : m(0) { } > + constexpr explicit B(A&&) : m(1) { } > + constexpr explicit B(const A&) : m(2) { } > + constexpr explicit B(const A&&) : m(3) { } > + }; > + > + // template<tuple-like UTuple> > + // constexpr explicit(true) tuple(UTuple&&); > + > + static_assert( !std::is_convertible_v<tuple_like_t<A>, tuple<B, B, B>> ); > + > + tuple_like_t<A> tuple_like; > + > + [&] { > + tuple<B, B, B> t3b{tuple_like}; > + VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && > std::get<2>(t3b).m == 0 ); > + }(); > + [&] { > + tuple<B, B, B> t3b{std::move(tuple_like)}; > + VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && > std::get<2>(t3b).m == 1 ); > + }(); > + [&] { > + tuple<B, B, B> t3b{std::as_const(tuple_like)}; > + VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && > std::get<2>(t3b).m == 2 ); > + }(); > + [&] { > + tuple<B, B, B> t3b{std::move(std::as_const(tuple_like))}; > + VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && > std::get<2>(t3b).m == 3 ); > + }(); > + > + return true; > +} > + > +namespace alloc > +{ > + struct B02 { > + int m; > + explicit B02(A&); > + explicit B02(A&&); > + explicit B02(const A&); > + explicit B02(const A&&); > + explicit constexpr B02(allocator_arg_t, allocator<int>, A&) : m(0) { } > + explicit constexpr B02(allocator_arg_t, allocator<int>, A&&) : m(1) { } > + explicit constexpr B02(allocator_arg_t, allocator<int>, const A&) : m(2) > { } > + explicit constexpr B02(allocator_arg_t, allocator<int>, const A&&) : > m(3) { } > + }; > + > + template<template<typename> class tuple_like_t> > + constexpr bool > + test02() > + { > + using B = B02; > + > + // template<tuple-like UTuple> > + // constexpr explicit(true) tuple(allocator_arg_t, const Alloc&, > UTuple&&); > + > + static_assert( !std::is_convertible_v<tuple_like_t<A>, tuple<B, B, B>> ); > + > + tuple_like_t<A> tuple_like; > + > + [&] { > + tuple<B, B, B> t3b{allocator_arg, allocator<int>{}, tuple_like}; > + VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && > std::get<2>(t3b).m == 0 ); > + }(); > + [&] { > + tuple<B, B, B> t3b{allocator_arg, allocator<int>{}, > std::move(tuple_like)}; > + VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && > std::get<2>(t3b).m == 1 ); > + }(); > + [&] { > + tuple<B, B, B> t3b{allocator_arg, allocator<int>{}, > std::as_const(tuple_like)}; > + VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && > std::get<2>(t3b).m == 2 ); > + }(); > + [&] { > + tuple<B, B, B> t3b{allocator_arg, allocator<int>{}, > std::move(std::as_const(tuple_like))}; > + VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && > std::get<2>(t3b).m == 3 ); > + }(); > + > + return true; > + } > +} > + > + > +template<template<typename> class tuple_like_t> > +constexpr bool > +test03() > +{ > + struct B { > + int m; > + constexpr B& operator=(A&) { m = 0; return *this; } > + constexpr B& operator=(A&&) { m = 1; return *this; } > + constexpr B& operator=(const A&) { m = 2; return *this; } > + constexpr B& operator=(const A&&) { m = 3; return *this; } > + }; > + > + // template<tuple-like UTuple> > + // constexpr tuple& operator=(UTuple&&); > + > + tuple_like_t<A> tuple_like; > + > + tuple<B, B, B> t3b; > + t3b = tuple_like; > + VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && > std::get<2>(t3b).m == 0 ); > + t3b = std::move(tuple_like); > + VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && > std::get<2>(t3b).m == 1 ); > + t3b = std::as_const(tuple_like); > + VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && > std::get<2>(t3b).m == 2 ); > + t3b = std::move(std::as_const(tuple_like)); > + VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && > std::get<2>(t3b).m == 3 ); > + > + return true; > +} > + > +template<template<typename> class tuple_like_t> > +constexpr bool > +test04() > +{ > + struct B { > + mutable int m; > + constexpr const B& operator=(A&) const { m = 0; return *this; } > + constexpr const B& operator=(A&&) const { m = 1; return *this; } > + constexpr const B& operator=(const A&) const { m = 2; return *this; } > + constexpr const B& operator=(const A&&) const { m = 3; return *this; } > + }; > + > + // template<tuple-like UTuple> > + // constexpr const tuple& operator=(UTuple&&) const; > + > + tuple_like_t<A> tuple_like; > + > + const tuple<B, B, B> t3b; > + t3b = tuple_like; > + VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && > std::get<2>(t3b).m == 0 ); > + t3b = std::move(tuple_like); > + VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && > std::get<2>(t3b).m == 1 ); > + t3b = std::as_const(tuple_like); > + VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && > std::get<2>(t3b).m == 2 ); > + t3b = std::move(std::as_const(tuple_like)); > + VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && > std::get<2>(t3b).m == 3 ); > + > + return true; > +} > + > +template<template<typename> class tuple_like_t> > +constexpr bool > +test05() > +{ > + // template<tuple-like UTuple> > + // constexpr bool operator==(const tuple&, const UTuple&); > + > + static_assert( tuple{1, 2, 3} == tuple_like_t{1, 2, 3} ); > + static_assert( tuple{1, 2, 4} != tuple_like_t{1, 2, 3} ); > + static_assert( tuple_like_t{1, 2, 3} == tuple{1, 2, 3} ); > + static_assert( tuple_like_t{1, 2, 3} != tuple{1, 2, 4} ); > + > + // template<tuple-like UTuple> > + // constexpr bool operator<=>const tuple&, const UTuple&); > + > + static_assert( (tuple{1, 2, 3} <=> tuple_like_t{1, 2, 3}) == > std::strong_ordering::equal ); > + static_assert( (tuple{1, 2, 4} <=> tuple_like_t{1, 2, 3}) == > std::strong_ordering::greater ); > + static_assert( (tuple_like_t{1, 2, 3} <=> tuple{1, 2, 3}) == > std::strong_ordering::equal ); > + static_assert( (tuple_like_t{1, 2, 3} <=> tuple{1, 2, 4}) == > std::strong_ordering::less ); > + > + static_assert( tuple{1, 2, 4} > tuple_like_t{1, 2, 3} ); > + static_assert( tuple_like_t{1, 2, 3} < tuple{1, 2, 4} ); > + > + // template<tuple-like TTuple, tuple-like UTuple, ...> > + // struct basic_common_reference<TTuple, UTuple, ...>; > + > + static_assert( std::same_as<std::common_reference_t<tuple_like_t<int>, > + tuple<int, long, int>>, > + tuple<int, long, int>> ); > + > + static_assert( std::same_as<std::common_reference_t<tuple<int, long, int>, > + tuple_like_t<int>>, > + tuple<int, long, int>> ); > + > + // template<tuple-like TTuple, tuple-like UTuple> > + // struct common_type<TTuple, UTuple>; > + > + static_assert( std::same_as<std::common_type_t<tuple_like_t<const int&>, > + tuple<int, long, int>>, > + tuple<int, long, int>> ); > + > + static_assert( std::same_as<std::common_type_t<tuple<int, long, int>, > + tuple_like_t<const int&>>, > + tuple<int, long, int>> ); > + > + return true; > +} > + > +template<typename T> > +using tuple_like_array = array<T, 3>; > + > +int > +main() > +{ > + static_assert( test01<tuple_like_array>() ); > + static_assert( alloc::test01<tuple_like_array>() ); > + static_assert( test02<tuple_like_array>() ); > + static_assert( alloc::test02<tuple_like_array>() ); > + static_assert( test03<tuple_like_array>() ); > + static_assert( test04<tuple_like_array>() ); > + static_assert( test05<tuple_like_array>() ); > +} > diff --git a/libstdc++-v3/testsuite/std/ranges/zip/1.cc > b/libstdc++-v3/testsuite/std/ranges/zip/1.cc > index b7717aed92c..672a8c356d9 100644 > --- a/libstdc++-v3/testsuite/std/ranges/zip/1.cc > +++ b/libstdc++-v3/testsuite/std/ranges/zip/1.cc > @@ -41,8 +41,8 @@ test01() > VERIFY( i2 == z2.end() ); > VERIFY( ranges::size(z2) == 2 ); > VERIFY( ranges::size(std::as_const(z2)) == 2 ); > - VERIFY( z2[0].first == 1 && z2[0].second == 3 ); > - VERIFY( z2[1].first == 2 && z2[1].second == 4 ); > + VERIFY( std::get<0>(z2[0]) == 1 && std::get<1>(z2[0]) == 3 ); > + VERIFY( std::get<0>(z2[1]) == 2 && std::get<1>(z2[1]) == 4 ); > for (const auto [x, y] : z2) > { > VERIFY( y - x == 2 ); > -- > 2.43.0.386.ge02ecfcc53 > >