From: Patrick Palka <ppalka@redhat.com>
To: Patrick Palka <ppalka@redhat.com>
Cc: Jonathan Wakely <jwakely@redhat.com>,
gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org
Subject: Re: [PATCH 2/2] libstdc++: Implement P2165R4 changes to std::pair/tuple/etc
Date: Wed, 31 Jan 2024 14:41:50 -0500 (EST) [thread overview]
Message-ID: <a7afc942-877e-f4ef-41c2-d6e1fb7d39b5@idea> (raw)
In-Reply-To: <8c02178f-0e1c-80dd-6bbf-b880e4ec0a63@idea>
[-- Attachment #1: Type: text/plain, Size: 88392 bytes --]
On Wed, 31 Jan 2024, Patrick Palka wrote:
> 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 <ppalka@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.
... now also as an attachment since it seems Gmail doesn't like my
inline patch.
>
> >
> > -- >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
> >
> >
>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: Type: text/x-diff; name=0001-libstdc-Implement-P2165R4-changes-to-std-pair-tuple-.patch, Size: 61811 bytes --]
From c1189dc4a003d218f892d9c6642edf273496ab0c Mon Sep 17 00:00:00 2001
From: Patrick Palka <ppalka@redhat.com>
Date: Wed, 24 Jan 2024 17:17:39 -0500
Subject: [PATCH] libstdc++: Implement P2165R4 changes to std::pair/tuple/etc
[PR113309]
Tested on x86_64-pc-linux-gnu, does this look OK for trunK?
-- >8 --
This implements the C++23 paper P2165R4 Compatibility between tuple,
pair and tuple-like objects, which builds upon many changes from the
earlier C++23 paper P2321R2 zip.
Some declarations had to be moved around so that they're visible from
<bits/stl_pair.h> without bloating the header. In the end, the
only new include is for <bits/utility.h> from <bits/stl_iterator.h>,
for tuple_element_t.
PR libstdc++/113309
PR libstdc++/109203
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.
(test05): New test.
* 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 | 17 +-
15 files changed, 1069 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..ea4274d267b 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 );
@@ -124,6 +124,18 @@ test04()
return true;
}
+constexpr bool
+test05()
+{
+ // PR libstdc++/109203
+ int x[] = {1, 1, 2};
+ int y[] = {2, 1, 3};
+ auto r = views::zip(x, y);
+ ranges::sort(r);
+
+ return true;
+}
+
int
main()
{
@@ -131,4 +143,5 @@ main()
static_assert(test02());
static_assert(test03());
static_assert(test04());
+ static_assert(test05());
}
--
2.43.0.493.gbc7ee2e5e1
next prev parent reply other threads:[~2024-01-31 19:41 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-01-23 23:53 [PATCH 1/2] libstdc++/pair: Define _S_const_assignable helper for C++20 Patrick Palka
2024-01-23 23:53 ` [PATCH 2/2] libstdc++: Implement P2165R4 changes to std::pair/tuple/etc Patrick Palka
2024-01-24 12:01 ` Jonathan Wakely
2024-01-24 12:08 ` Jonathan Wakely
2024-01-24 15:24 ` Patrick Palka
2024-01-24 17:27 ` Jonathan Wakely
2024-01-24 18:57 ` Patrick Palka
2024-01-24 19:47 ` Patrick Palka
2024-01-31 19:39 ` Patrick Palka
2024-01-31 19:41 ` Patrick Palka [this message]
2024-01-31 21:55 ` Jonathan Wakely
2024-01-24 21:16 ` Jonathan Wakely
2024-01-24 0:21 ` [PATCH 1/2] libstdc++/pair: Define _S_const_assignable helper for C++20 Jonathan Wakely
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=a7afc942-877e-f4ef-41c2-d6e1fb7d39b5@idea \
--to=ppalka@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jwakely@redhat.com \
--cc=libstdc++@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).