On Wed, 24 Jan 2024, Patrick Palka wrote:

> On Wed, 24 Jan 2024, Patrick Palka wrote:
> 
> > On Wed, 24 Jan 2024, Jonathan Wakely wrote:
> > 
> > > On Wed, 24 Jan 2024 at 15:24, Patrick Palka <ppa...@redhat.com> wrote:
> > > >
> > > > On Wed, 24 Jan 2024, Jonathan Wakely wrote:
> > > >
> > > > > On Tue, 23 Jan 2024 at 23:54, Patrick Palka wrote:
> > > > > > diff --git a/libstdc++-v3/include/bits/stl_pair.h 
> > > > > > b/libstdc++-v3/include/bits/stl_pair.h
> > > > > > index b81b479ad43..a9b20fbe7ca 100644
> > > > > > --- a/libstdc++-v3/include/bits/stl_pair.h
> > > > > > +++ b/libstdc++-v3/include/bits/stl_pair.h
> > > > > > @@ -85,12 +85,70 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >    /// @cond undocumented
> > > > > >
> > > > > >    // Forward declarations.
> > > > > > +  template<typename, typename>
> > > > > > +    struct pair;
> > > > >
> > > > > We have a compiler bug where a forward declaration without template
> > > > > parameter names causes bad diagnostics later. The compiler seems to
> > > > > try to use the parameter names from the first decl it sees, so we end
> > > > > up with things like <template-argument-1-1> even when there's a name
> > > > > available at the site of the actual error. So I think we should name
> > > > > these _T1 and _T2 here.
> > > >
> > > > Will fix.
> > > >
> > > > >
> > > > > > +
> > > > > >    template<typename...>
> > > > > >      class tuple;
> > > > > >
> > > > > > +  // Declarations of std::array and its std::get overloads, so that
> > > > > > +  // std::tuple_cat can use them if <tuple> is included before 
> > > > > > <array>.
> > > > > > +  // We also declare the other std::get overloads here so that 
> > > > > > they're
> > > > > > +  // visible to the P2165R4 tuple-like constructors of pair and 
> > > > > > tuple.
> > > > > > +  template<typename _Tp, size_t _Nm>
> > > > > > +    struct array;
> > > > > > +
> > > > > >    template<size_t...>
> > > > > >      struct _Index_tuple;
> > > > > >
> > > > > > +  template<size_t _Int, class _Tp1, class _Tp2>
> > > > > > +    constexpr typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type&
> > > > > > +    get(pair<_Tp1, _Tp2>& __in) noexcept;
> > > > > > +
> > > > > > +  template<size_t _Int, class _Tp1, class _Tp2>
> > > > > > +    constexpr typename tuple_element<_Int, pair<_Tp1, 
> > > > > > _Tp2>>::type&&
> > > > > > +    get(pair<_Tp1, _Tp2>&& __in) noexcept;
> > > > > > +
> > > > > > +  template<size_t _Int, class _Tp1, class _Tp2>
> > > > > > +    constexpr const typename tuple_element<_Int, pair<_Tp1, 
> > > > > > _Tp2>>::type&
> > > > > > +    get(const pair<_Tp1, _Tp2>& __in) noexcept;
> > > > > > +
> > > > > > +  template<size_t _Int, class _Tp1, class _Tp2>
> > > > > > +    constexpr const typename tuple_element<_Int, pair<_Tp1, 
> > > > > > _Tp2>>::type&&
> > > > > > +    get(const pair<_Tp1, _Tp2>&& __in) noexcept;
> > > > > > +
> > > > > > +  template<size_t __i, typename... _Elements>
> > > > > > +    constexpr __tuple_element_t<__i, tuple<_Elements...>>&
> > > > > > +    get(tuple<_Elements...>& __t) noexcept;
> > > > > > +
> > > > > > +  template<size_t __i, typename... _Elements>
> > > > > > +    constexpr const __tuple_element_t<__i, tuple<_Elements...>>&
> > > > > > +    get(const tuple<_Elements...>& __t) noexcept;
> > > > > > +
> > > > > > +  template<size_t __i, typename... _Elements>
> > > > > > +    constexpr __tuple_element_t<__i, tuple<_Elements...>>&&
> > > > > > +    get(tuple<_Elements...>&& __t) noexcept;
> > > > > > +
> > > > > > +  template<size_t __i, typename... _Elements>
> > > > > > +    constexpr const __tuple_element_t<__i, tuple<_Elements...>>&&
> > > > > > +    get(const tuple<_Elements...>&& __t) noexcept;
> > > > > > +
> > > > > > +  template<size_t _Int, typename _Tp, size_t _Nm>
> > > > > > +    constexpr _Tp&
> > > > > > +    get(array<_Tp, _Nm>&) noexcept;
> > > > > > +
> > > > > > +  template<size_t _Int, typename _Tp, size_t _Nm>
> > > > > > +    constexpr _Tp&&
> > > > > > +    get(array<_Tp, _Nm>&&) noexcept;
> > > > > > +
> > > > > > +  template<size_t _Int, typename _Tp, size_t _Nm>
> > > > > > +    constexpr const _Tp&
> > > > > > +    get(const array<_Tp, _Nm>&) noexcept;
> > > > > > +
> > > > > > +  template<size_t _Int, typename _Tp, size_t _Nm>
> > > > > > +    constexpr const _Tp&&
> > > > > > +    get(const array<_Tp, _Nm>&&) noexcept;
> > > > > > +
> > > > > >  #if ! __cpp_lib_concepts
> > > > > >    // Concept utility functions, reused in conditionally-explicit
> > > > > >    // constructors.
> > > > > > @@ -159,6 +217,46 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >  #endif // lib concepts
> > > > > >  #endif // C++11
> > > > > >
> > > > > > +#if __glibcxx_tuple_like // >= C++23
> > > > > > +  template<typename _Tp>
> > > > > > +    inline constexpr bool __is_tuple_v = false;
> > > > > > +
> > > > > > +  template<typename... _Ts>
> > > > > > +    inline constexpr bool __is_tuple_v<tuple<_Ts...>> = true;
> > > > > > +
> > > > > > +  // TODO: Reuse __is_tuple_like from <type_traits>?
> > > > > > +  template<typename _Tp>
> > > > > > +    inline constexpr bool __is_tuple_like_v = false;
> > > > > > +
> > > > > > +  template<typename... _Elements>
> > > > > > +    inline constexpr bool __is_tuple_like_v<tuple<_Elements...>> = 
> > > > > > true;
> > > > > > +
> > > > > > +  template<typename _T1, typename _T2>
> > > > > > +    inline constexpr bool __is_tuple_like_v<pair<_T1, _T2>> = true;
> > > > > > +
> > > > > > +  template<typename _Tp, size_t _Nm>
> > > > > > +    inline constexpr bool __is_tuple_like_v<array<_Tp, _Nm>> = 
> > > > > > true;
> > > > > > +
> > > > > > +  // __is_tuple_like_v<subrange> is defined in 
> > > > > > <bits/ranges_util.h>.
> > > > > > +
> > > > > > +  template<typename _Tp>
> > > > > > +    concept __tuple_like = __is_tuple_like_v<remove_cvref_t<_Tp>>;
> > > > > > +
> > > > > > +  template<typename _Tp>
> > > > > > +    concept __pair_like = __tuple_like<_Tp> && 
> > > > > > tuple_size_v<remove_cvref_t<_Tp>> == 2;
> > > > > > +
> > > > > > +  template<typename _Tp, typename _Tuple>
> > > > > > +    concept __eligible_tuple_like
> > > > > > +      = __detail::__different_from<_Tp, _Tuple> && 
> > > > > > __tuple_like<_Tp>
> > > > > > +       && (tuple_size_v<remove_cvref_t<_Tp>> == 
> > > > > > tuple_size_v<_Tuple>)
> > > > > > +       && !ranges::__detail::__is_subrange<remove_cvref_t<_Tp>>;
> > > > > > +
> > > > > > +  template<typename _Tp, typename _Pair>
> > > > > > +    concept __eligible_pair_like
> > > > > > +      = __detail::__different_from<_Tp, _Pair> && __pair_like<_Tp>
> > > > > > +       && !ranges::__detail::__is_subrange<remove_cvref_t<_Tp>>;
> > > > > > +#endif // C++23
> > > > > > +
> > > > > >    template<typename _U1, typename _U2> class __pair_base
> > > > > >    {
> > > > > >  #if __cplusplus >= 201103L && ! __cpp_lib_concepts
> > > > > > @@ -295,6 +393,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >           return false;
> > > > > >  #endif
> > > > > >         }
> > > > > > +
> > > > > > +#if __glibcxx_tuple_like // >= C++23
> > > > > > +      template<typename _UPair>
> > > > > > +       static constexpr bool
> > > > > > +       _S_constructible_from_pair_like()
> > > > > > +       {
> > > > > > +         return 
> > > > > > _S_constructible<decltype(std::get<0>(std::declval<_UPair>())),
> > > > > > +                                 
> > > > > > decltype(std::get<1>(std::declval<_UPair>()))>();
> > > > > > +       }
> > > > > > +
> > > > > > +      template<typename _UPair>
> > > > > > +       static constexpr bool
> > > > > > +       _S_convertible_from_pair_like()
> > > > > > +       {
> > > > > > +         return 
> > > > > > _S_convertible<decltype(std::get<0>(std::declval<_UPair>())),
> > > > > > +                               
> > > > > > decltype(std::get<1>(std::declval<_UPair>()))>();
> > > > > > +       }
> > > > > > +#endif // C++23
> > > > > >        /// @endcond
> > > > > >
> > > > > >      public:
> > > > > > @@ -393,6 +509,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >         pair(const pair<_U1, _U2>&&) = delete;
> > > > > >  #endif // C++23
> > > > > >
> > > > > > +#if __glibcxx_tuple_like // >= C++23
> > > > > > +      template<__eligible_pair_like<pair> _UPair>
> > > > > > +       requires (_S_constructible_from_pair_like<_UPair>())
> > > > > > +       constexpr explicit(!_S_convertible_from_pair_like<_UPair>())
> > > > > > +       pair(_UPair&& __p)
> > > > > > +       : first(std::get<0>(std::forward<_UPair>(__p))),
> > > > > > +         second(std::get<1>(std::forward<_UPair>(__p)))
> > > > > > +       { }
> > > > > > +#endif // C++23
> > > > >
> > > > > I think this needs to be constrained with !_S_dangles<...>() and we
> > > > > need a deleted overload with the same constraints, except for
> > > > > _S_dangles being true.
> > > > >
> > > > > And that should be covered by a test.
> > > >
> > > > Oops, will fix.  Should the deleted overloads carry over the
> > > > conditionally explicit specifier?  I noticed pair's deleted overloads
> > > > do, but tuple's overloads don't.
> > > 
> > > Huh, oops. Does an explicit ctor participate in overload resolution
> > > and then get checked if it's usable, or is it remove from overload
> > > resolution earlier?
> > > It looks like I decided the answer was the latter for pair and the
> > > former for tuple.
> > 
> > AFAICT if we know the explicitness of the ctor early (either because the
> > ctor is a non-template or it has a non-dependent explicit-spec), then we
> > remove it from the overload set early, in which case it'd be useful to
> > give the deleted ctor the right explicit-spec to avoid unecessary
> > constraint checking etc.
> > 
> > Otherwise, for ctor templates with a dependent explicit-spec such as
> > these tuple/pair ones, we must wait until after deduction to check
> > explicitness which means constraints are checked first.  And by then
> > we already know that __dangles is true, so we presumably want overload
> > resolution to fail regardless.  Whether that's due to selecting a
> > (viable) deleted non-explicit ctor (if we omit the explicit-spec) or due
> > to there being no viable non-explicit ctor (if we carry over the
> > explicit-spec) shouldn't make a difference, I think?
> > 
> > So it seems unnecessary to give these deleted overloads an
> > explicit-spec; it wouldn't be considered unless overload resolution
> > is destined to fail anyway.
> > 
> > I'm not totally confident about this assessment though so I'll
> > just carry over the explicit-spec for now.
> 
> I ended up hedging my bets and including the explicit-spec in the
> deleted ctors of pair and omitting it in those of tuple, so that
> we continue to be locally consistent.
> 
> > 
> > > 
> > > >
> > > > >
> > > > >
> > > > >
> > > > > > diff --git a/libstdc++-v3/include/std/tuple 
> > > > > > b/libstdc++-v3/include/std/tuple
> > > > > > index be92f1eb973..182f3cc5e6a 100644
> > > > > > --- a/libstdc++-v3/include/std/tuple
> > > > > > +++ b/libstdc++-v3/include/std/tuple
> > > > > > @@ -50,6 +50,7 @@
> > > > > >  #define __glibcxx_want_apply
> > > > > >  #define __glibcxx_want_make_from_tuple
> > > > > >  #define __glibcxx_want_ranges_zip
> > > > > > +#define __glibcxx_want_tuple_like
> > > > > >  #include <bits/version.h>
> > > > > >
> > > > > >  namespace std _GLIBCXX_VISIBILITY(default)
> > > > > > @@ -246,6 +247,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >        _Head _M_head_impl;
> > > > > >      };
> > > > > >
> > > > > > +#if __cpp_lib_tuple_like // >= C++23
> > > > > > +  struct __tuple_like_tag_t { explicit __tuple_like_tag_t() = 
> > > > > > default; };
> > > > > > +
> > > > > > +  // Forward declared for use by the operator<=> overload for 
> > > > > > tuple-like types.
> > > > > > +  template<typename _Cat, typename _Tp, typename _Up>
> > > > > > +    constexpr _Cat
> > > > > > +    __tuple_cmp(const _Tp&, const _Up&, index_sequence<>);
> > > > > > +
> > > > > > +  template<typename _Cat, typename _Tp, typename _Up,
> > > > > > +          size_t _Idx0, size_t... _Idxs>
> > > > > > +    constexpr _Cat
> > > > > > +    __tuple_cmp(const _Tp& __t, const _Up& __u,
> > > > > > +               index_sequence<_Idx0, _Idxs...>);
> > > > > > +#endif // C++23
> > > > > > +
> > > > > >    /**
> > > > > >     * Contains the actual implementation of the @c tuple template, 
> > > > > > stored
> > > > > >     * as a recursive inheritance hierarchy from the first element 
> > > > > > (most
> > > > > > @@ -342,6 +358,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >         { }
> > > > > >  #endif // C++23
> > > > > >
> > > > > > +#if __cpp_lib_tuple_like // >= C++23
> > > > > > +      template<typename _UTuple, size_t... _Is>
> > > > > > +       constexpr
> > > > > > +       _Tuple_impl(__tuple_like_tag_t, _UTuple&& __u, 
> > > > > > index_sequence<_Is...>)
> > > > > > +       : _Tuple_impl(std::get<_Is>(std::forward<_UTuple>(__u))...)
> > > > > > +       { }
> > > > > > +#endif // C++23
> > > > > > +
> > > > > >        template<typename _Alloc>
> > > > > >         _GLIBCXX20_CONSTEXPR
> > > > > >         _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a)
> > > > > > @@ -428,6 +452,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >         { }
> > > > > >  #endif // C++23
> > > > > >
> > > > > > +#if __cpp_lib_tuple_like // >= C++23
> > > > > > +      template<typename _Alloc, typename _UTuple, size_t... _Is>
> > > > > > +       constexpr
> > > > > > +       _Tuple_impl(__tuple_like_tag_t, allocator_arg_t __tag, 
> > > > > > const _Alloc& __a,
> > > > > > +                   _UTuple&& __u, index_sequence<_Is...>)
> > > > > > +       : _Tuple_impl(__tag, __a, 
> > > > > > std::get<_Is>(std::forward<_UTuple>(__u))...)
> > > > > > +       { }
> > > > > > +#endif // C++23
> > > > > > +
> > > > > >        template<typename... _UElements>
> > > > > >         _GLIBCXX20_CONSTEXPR
> > > > > >         void
> > > > > > @@ -470,6 +503,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >         }
> > > > > >  #endif // C++23
> > > > > >
> > > > > > +#if __cpp_lib_tuple_like // >= C++23
> > > > > > +      template<typename _UTuple>
> > > > > > +       constexpr void
> > > > > > +       _M_assign(__tuple_like_tag_t __tag, _UTuple&& __u)
> > > > > > +       {
> > > > > > +         _M_head(*this) = 
> > > > > > std::get<_Idx>(std::forward<_UTuple>(__u));
> > > > > > +         _M_tail(*this)._M_assign(__tag, 
> > > > > > std::forward<_UTuple>(__u));
> > > > > > +       }
> > > > > > +
> > > > > > +      template<typename _UTuple>
> > > > > > +       constexpr void
> > > > > > +       _M_assign(__tuple_like_tag_t __tag, _UTuple&& __u) const
> > > > > > +       {
> > > > > > +         _M_head(*this) = 
> > > > > > std::get<_Idx>(std::forward<_UTuple>(__u));
> > > > > > +         _M_tail(*this)._M_assign(__tag, 
> > > > > > std::forward<_UTuple>(__u));
> > > > > > +       }
> > > > > > +#endif // C++23
> > > > > > +
> > > > > >      protected:
> > > > > >        _GLIBCXX20_CONSTEXPR
> > > > > >        void
> > > > > > @@ -563,6 +614,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >         { }
> > > > > >  #endif // C++23
> > > > > >
> > > > > > +#if __cpp_lib_tuple_like // >= C++23
> > > > > > +      template<typename _UTuple>
> > > > > > +       constexpr
> > > > > > +       _Tuple_impl(__tuple_like_tag_t, _UTuple&& __u, 
> > > > > > index_sequence<0>)
> > > > > > +       : _Tuple_impl(std::get<0>(std::forward<_UTuple>(__u)))
> > > > > > +       { }
> > > > > > +#endif // C++23
> > > > > > +
> > > > > >        template<typename _Alloc>
> > > > > >         _GLIBCXX20_CONSTEXPR
> > > > > >         _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a)
> > > > > > @@ -633,6 +692,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >         { }
> > > > > >  #endif // C++23
> > > > > >
> > > > > > +#if __cpp_lib_tuple_like // >= C++23
> > > > > > +      template<typename _Alloc, typename _UTuple>
> > > > > > +       constexpr
> > > > > > +       _Tuple_impl(__tuple_like_tag_t, allocator_arg_t __tag, 
> > > > > > const _Alloc& __a,
> > > > > > +                   _UTuple&& __u, index_sequence<0>)
> > > > > > +       : _Tuple_impl(__tag, __a, 
> > > > > > std::get<0>(std::forward<_UTuple>(__u)))
> > > > > > +       { }
> > > > > > +#endif // C++23
> > > > > > +
> > > > > >        template<typename _UHead>
> > > > > >         _GLIBCXX20_CONSTEXPR
> > > > > >         void
> > > > > > @@ -667,6 +735,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >         }
> > > > > >  #endif // C++23
> > > > > >
> > > > > > +#if __cpp_lib_tuple_like // >= C++23
> > > > > > +    template<typename _UTuple>
> > > > > > +      constexpr void
> > > > > > +      _M_assign(__tuple_like_tag_t, _UTuple&& __u)
> > > > > > +      { _M_head(*this) = 
> > > > > > std::get<_Idx>(std::forward<_UTuple>(__u)); }
> > > > > > +
> > > > > > +    template<typename _UTuple>
> > > > > > +      constexpr void
> > > > > > +      _M_assign(__tuple_like_tag_t, _UTuple&& __u) const
> > > > > > +      { _M_head(*this) = 
> > > > > > std::get<_Idx>(std::forward<_UTuple>(__u)); }
> > > > > > +#endif // C++23
> > > > > > +
> > > > > >      protected:
> > > > > >        _GLIBCXX20_CONSTEXPR
> > > > > >        void
> > > > > > @@ -846,6 +926,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >  #endif
> > > > > >         }
> > > > > >
> > > > > > +#if __cpp_lib_tuple_like // >= C++23
> > > > > > +      template<typename _UTuple>
> > > > > > +       static consteval bool
> > > > > > +       __constructible_from_tuple_like()
> > > > > > +       {
> > > > > > +         return []<size_t... _Is>(index_sequence<_Is...>) {
> > > > > > +           return 
> > > > > > __constructible<decltype(std::get<_Is>(std::declval<_UTuple>()))...>();
> > > > > > +         }(make_index_sequence<sizeof...(_Elements)>{});
> > > > > > +       }
> > > > > > +
> > > > > > +      template<typename _UTuple>
> > > > > > +       static consteval bool
> > > > > > +       __convertible_from_tuple_like()
> > > > > > +       {
> > > > > > +         return []<size_t... _Is>(index_sequence<_Is...>) {
> > > > > > +           return 
> > > > > > __convertible<decltype(std::get<_Is>(std::declval<_UTuple>()))...>();
> > > > > > +         }(make_index_sequence<sizeof...(_Elements)>{});
> > > > >
> > > > > These new functions can use index_sequence_for<_Elements...>{} here,
> > > > > so you don't need the sizeof....
> > > > > That applies several times below as well.
> > > > >
> > > > > I think it's semantically identical, just a little shorter. I don't
> > > > > know if there's any compilation speed benefit either way. Maybe
> > > > > sizeof...(_Elements) is cheaper than expanding the pack into the
> > > > > index_sequence_for alias template?
> 
> It probably a little cheaper to use make_index_sequence directly, but I
> just didn't know about index_sequence_for :) Consider that changed.
> 
> > > > >
> > > > >
> > > > > > +       }
> > > > > > +#endif // C++23
> > > > > > +
> > > > > >      public:
> > > > > >        constexpr
> > > > > >        
> > > > > > explicit(!(__is_implicitly_default_constructible_v<_Elements> && 
> > > > > > ...))
> > > > > > @@ -1016,10 +1116,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >         tuple(const pair<_U1, _U2>&&) = delete;
> > > > > >  #endif // C++23
> > > > > >
> > > > > > -#if 0 && __cpp_lib_tuple_like // >= C++23
> > > > > > -      template<__tuple_like _UTuple>
> > > > > > -       constexpr explicit(...)
> > > > > > -       tuple(_UTuple&& __u);
> > > > > > +#if __cpp_lib_tuple_like // >= C++23
> > > > > > +      template<__eligible_tuple_like<tuple> _UTuple>
> > > > > > +       requires (__constructible_from_tuple_like<_UTuple>())
> > > > > > +         && (!__use_other_ctor<_UTuple>())
> > > > > > +       constexpr 
> > > > > > explicit(!__convertible_from_tuple_like<_UTuple>())
> > > > > > +       tuple(_UTuple&& __u)
> > > > > > +       : _Inherited(__tuple_like_tag_t{},
> > > > > > +                    std::forward<_UTuple>(__u),
> > > > > > +                    make_index_sequence<sizeof...(_Elements)>{})
> > > > > > +       { }
> > > > > >  #endif // C++23
> > > > > >
> > > > > >        // Allocator-extended constructors.
> > > > > > @@ -1202,10 +1308,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > > >         tuple(allocator_arg_t, const _Alloc&, const pair<_U1, 
> > > > > > _U2>&&) = delete;
> > > > > >  #endif // C++23
> > > > > >
> > > > > > -#if 0 && __cpp_lib_tuple_like // >= C++23
> > > > > > -      template<typename _Alloc, __tuple_like _UTuple>
> > > > > > -       constexpr explicit(...)
> > > > > > -       tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& 
> > > > > > __u);
> > > > > > +#if __cpp_lib_tuple_like // >= C++23
> > > > > > +      template<typename _Alloc, __eligible_tuple_like<tuple> 
> > > > > > _UTuple>
> > > > > > +       requires (__constructible_from_tuple_like<_UTuple>())
> > > > > > +         && (!__use_other_ctor<_UTuple>())
> > > > > > +       constexpr 
> > > > > > explicit(!__convertible_from_tuple_like<_UTuple>())
> > > > > > +       tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& 
> > > > > > __u)
> > > > > > +       : _Inherited(__tuple_like_tag_t{},
> > > > > > +                    __tag, __a, std::forward<_UTuple>(__u),
> > > > > > +                    make_index_sequence<sizeof...(_Elements)>{})
> > > > > > +       { }
> > > > > >  #endif // C++23
> > > > >
> > > > > For some reason these two new constructors aren't deleted if they
> > > > > create dangling refs. I don't know why.
> > > >
> > > > Hmm, seems like an oversight.  Shall we proactively implement them?
> > > 
> > > Yes, I think so. I can't see why we would want to permit a dangling
> > > reference there.
> > > 
> > > e.g.
> > > std::array<long, 1> a{};
> > > std::tuple<const int&> t(a);
> > 
> > Sounds good.
> 
> In v2:
> 
> * Named the template parameters of the forward declaration of pair.
> * Added dangling checks for the new tuple and pair constructors
>   and corresponding tests.
> * Replaced make_index_sequence with index_sequence_for where applicable.

Ping.

> 
> -- >8 --
> 
> Subject: [PATCH 2/2] libstdc++: Implement P2165R4 changes to
>  std::pair/tuple/etc
> 
> libstdc++-v3/ChangeLog:
> 
>       * include/bits/ranges_util.h (__detail::__pair_like): Don't
>       define in C++23 mode.
>       (__detail::__pair_like_convertible_from): Adjust as per P2165R4.
>       (__detail::__is_subrange<subrange>): Moved from <ranges>.
>       (__detail::__is_tuple_like_v<subrange>): Likewise.
>       * include/bits/stl_iterator.h: Include <bits/utility.h> for
>       C++23.
>       (__different_from): Move to <concepts>.
>       (__iter_key_t): Adjust for C++23 as per P2165R4.
>       (__iter_val_t): Likewise.
>       * include/bits/stl_pair.h (pair, array): Forward declare.
>       (get): Forward declare all overloads relevant to P2165R4
>       tuple-like constructors.
>       (__is_tuple_v): Define for C++23.
>       (__is_tuple_like_v): Define for C++23.
>       (__tuple_like): Define for C++23 as per P2165R4.
>       (__pair_like): Define for C++23 as per P2165R4.
>       (__eligibile_tuple_like): Define for C++23.
>       (__eligibile_pair_like): Define for C++23.
>       (pair::_S_constructible_from_pair_like): Define for C++23.
>       (pair::_S_convertible_from_pair_like): Define for C++23.
>       (pair::_S_dangles_from_pair_like): Define for C++23.
>       (pair::pair): Define overloads taking a tuple-like type for
>       C++23 as per P2165R4.
>       (pair::_S_assignable_from_tuple_like): Define for C++23.
>       (pair::_S_const_assignable_from_tuple_like): Define for C++23.
>       (pair::operator=): Define overloads taking a tuple-like type for
>       C++23 as per P2165R4.
>       * include/bits/utility.h (ranges::__detail::__is_subrange):
>       Moved from <ranges>.
>       * include/bits/version.def (tuple_like): Define for C++23.
>       * include/bits/version.h: Regenerate.
>       * include/std/concepts (__different_from): Moved from
>       <bits/stl_iterator.h>.
>       (ranges::__swap::__adl_swap): Clarify which __detail namespace.
>       * include/std/map (__cpp_lib_tuple_like): Define C++23.
>       * include/std/ranges (__detail::__is_subrange): Moved to
>       <bits/utility.h>.
>       (__detail::__is_subrange<subrange>): Moved to <bits/ranges_util.h>
>       (__detail::__has_tuple_element): Adjust for C++23 as per P2165R4.
>       (__detail::__tuple_or_pair): Remove as per P2165R4.  Replace all
>       uses with plain tuple as per P2165R4.
>       * include/std/tuple (__cpp_lib_tuple_like): Define for C++23.
>       (__tuple_like_tag_t): Define for C++23.
>       (__tuple_cmp): Forward declare for C++23.
>       (_Tuple_impl::_Tuple_impl): Define overloads taking
>       __tuple_like_tag_t and a tuple-like type for C++23.
>       (_Tuple_impl::_M_assign): Likewise.
>       (tuple::__constructible_from_tuple_like): Define for C++23.
>       (tuple::__convertible_from_tuple_like): Define for C++23.
>       (tuple::__dangles_from_tuple_like): Define for C++23.
>       (tuple::tuple): Define overloads taking a tuple-like type for
>       C++23 as per P2165R4.
>       (tuple::__assignable_from_tuple_like): Define for C++23.
>       (tuple::__const_assignable_from_tuple_like): Define for C++23.
>       (tuple::operator=): Define overloads taking a tuple-like type
>       for C++23 as per P2165R4.
>       (tuple::__tuple_like_common_comparison_category): Define for C++23.
>       (tuple::operator<=>): Define overload taking a tuple-like type
>       for C++23 as per P2165R4.
>       (array, get): Forward declarations moved to <bits/stl_pair.h>.
>       (tuple_cat): Constrain with __tuple_like for C++23 as per P2165R4.
>       (apply): Likewise.
>       (make_from_tuple): Likewise.
>       (__tuple_like_common_reference): Define for C++23.
>       (basic_common_reference): Adjust as per P2165R4.
>       (__tuple_like_common_type): Define for C++23.
>       (common_type): Adjust as per P2165R4.
>       * include/std/unordered_map (__cpp_lib_tuple_like): Define for
>       C++23.
>       * include/std/utility (__cpp_lib_tuple_like): Define for C++23.
>       * testsuite/std/ranges/zip/1.cc (test01): Adjust to handle pair
>       and 2-tuple interchangeably.
>       * testsuite/20_util/pair/p2165r4.cc: New test.
>       * testsuite/20_util/tuple/p2165r4.cc: New test.
> ---
>  libstdc++-v3/include/bits/ranges_util.h       |  17 +-
>  libstdc++-v3/include/bits/stl_iterator.h      |  16 +-
>  libstdc++-v3/include/bits/stl_pair.h          | 182 ++++++++++
>  libstdc++-v3/include/bits/utility.h           |   8 +
>  libstdc++-v3/include/bits/version.def         |   8 +
>  libstdc++-v3/include/bits/version.h           |  11 +
>  libstdc++-v3/include/std/concepts             |  11 +-
>  libstdc++-v3/include/std/map                  |   1 +
>  libstdc++-v3/include/std/ranges               |  48 +--
>  libstdc++-v3/include/std/tuple                | 323 ++++++++++++++---
>  libstdc++-v3/include/std/unordered_map        |   1 +
>  libstdc++-v3/include/std/utility              |   1 +
>  .../testsuite/20_util/pair/p2165r4.cc         | 173 +++++++++
>  .../testsuite/20_util/tuple/p2165r4.cc        | 335 ++++++++++++++++++
>  libstdc++-v3/testsuite/std/ranges/zip/1.cc    |   4 +-
>  15 files changed, 1056 insertions(+), 83 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/20_util/pair/p2165r4.cc
>  create mode 100644 libstdc++-v3/testsuite/20_util/tuple/p2165r4.cc
> 
> diff --git a/libstdc++-v3/include/bits/ranges_util.h 
> b/libstdc++-v3/include/bits/ranges_util.h
> index bb04c49f044..9b79c3a229d 100644
> --- a/libstdc++-v3/include/bits/ranges_util.h
> +++ b/libstdc++-v3/include/bits/ranges_util.h
> @@ -224,6 +224,10 @@ namespace ranges
>       && !__uses_nonqualification_pointer_conversion<decay_t<_From>,
>                                                      decay_t<_To>>;
>  
> +#if __glibcxx_tuple_like // >= C++23
> +    // P2165R4 version of __pair_like is defined in <bits/stl_pair.h>.
> +#else
> +    // C++20 version of __pair_like from P2321R2.
>      template<typename _Tp>
>        concept __pair_like
>       = !is_reference_v<_Tp> && requires(_Tp __t)
> @@ -235,10 +239,11 @@ namespace ranges
>         { get<0>(__t) } -> convertible_to<const tuple_element_t<0, _Tp>&>;
>         { get<1>(__t) } -> convertible_to<const tuple_element_t<1, _Tp>&>;
>       };
> +#endif
>  
>      template<typename _Tp, typename _Up, typename _Vp>
>        concept __pair_like_convertible_from
> -     = !range<_Tp> && __pair_like<_Tp>
> +     = !range<_Tp> && !is_reference_v<_Vp> && __pair_like<_Tp>
>       && constructible_from<_Tp, _Up, _Vp>
>       && __convertible_to_non_slicing<_Up, tuple_element_t<0, _Tp>>
>       && convertible_to<_Vp, tuple_element_t<1, _Tp>>;
> @@ -463,8 +468,18 @@ namespace ranges
>      using borrowed_subrange_t = __conditional_t<borrowed_range<_Range>,
>                                               subrange<iterator_t<_Range>>,
>                                               dangling>;
> +
> +  // __is_subrange is defined in <bits/utility.h>.
> +  template<typename _Iter, typename _Sent, subrange_kind _Kind>
> +    inline constexpr bool __detail::__is_subrange<subrange<_Iter, _Sent, 
> _Kind>> = true;
>  } // namespace ranges
>  
> +#if __glibcxx_tuple_like // >= C++23
> +  // __is_tuple_like_v is defined in <bits/stl_pair.h>.
> +  template<typename _It, typename _Sent, ranges::subrange_kind _Kind>
> +    inline constexpr bool __is_tuple_like_v<ranges::subrange<_It, _Sent, 
> _Kind>> = true;
> +#endif
> +
>  // The following ranges algorithms are used by <ranges>, and are defined here
>  // so that <ranges> can avoid including all of <bits/ranges_algo.h>.
>  namespace ranges
> diff --git a/libstdc++-v3/include/bits/stl_iterator.h 
> b/libstdc++-v3/include/bits/stl_iterator.h
> index d71a793e10d..560a10a7abe 100644
> --- a/libstdc++-v3/include/bits/stl_iterator.h
> +++ b/libstdc++-v3/include/bits/stl_iterator.h
> @@ -78,6 +78,10 @@
>  # include <bits/stl_construct.h>
>  #endif
>  
> +#if __glibcxx_tuple_like // >= C++23
> +# include <bits/utility.h> // for tuple_element_t
> +#endif
> +
>  namespace std _GLIBCXX_VISIBILITY(default)
>  {
>  _GLIBCXX_BEGIN_NAMESPACE_VERSION
> @@ -95,10 +99,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      template<typename _Cat, typename _Limit, typename _Otherwise = _Cat>
>        using __clamp_iter_cat
>       = __conditional_t<derived_from<_Cat, _Limit>, _Limit, _Otherwise>;
> -
> -    template<typename _Tp, typename _Up>
> -      concept __different_from
> -     = !same_as<remove_cvref_t<_Tp>, remove_cvref_t<_Up>>;
>    }
>  #endif
>  
> @@ -2983,11 +2983,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    // of associative containers.
>    template<typename _InputIterator>
>      using __iter_key_t = remove_const_t<
> +#if __glibcxx_tuple_like // >= C++23
> +      tuple_element_t<0, typename 
> iterator_traits<_InputIterator>::value_type>>;
> +#else
>        typename iterator_traits<_InputIterator>::value_type::first_type>;
> +#endif
>  
>    template<typename _InputIterator>
>      using __iter_val_t
> +#if __glibcxx_tuple_like // >= C++23
> +      = tuple_element_t<1, typename 
> iterator_traits<_InputIterator>::value_type>;
> +#else
>        = typename iterator_traits<_InputIterator>::value_type::second_type;
> +#endif
>  
>    template<typename _T1, typename _T2>
>      struct pair;
> diff --git a/libstdc++-v3/include/bits/stl_pair.h 
> b/libstdc++-v3/include/bits/stl_pair.h
> index b81b479ad43..00ec53ebc33 100644
> --- a/libstdc++-v3/include/bits/stl_pair.h
> +++ b/libstdc++-v3/include/bits/stl_pair.h
> @@ -85,12 +85,70 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    /// @cond undocumented
>  
>    // Forward declarations.
> +  template<typename _T1, typename _T2>
> +    struct pair;
> +
>    template<typename...>
>      class tuple;
>  
> +  // Declarations of std::array and its std::get overloads, so that
> +  // std::tuple_cat can use them if <tuple> is included before <array>.
> +  // We also declare the other std::get overloads here so that they're
> +  // visible to the P2165R4 tuple-like constructors of pair and tuple.
> +  template<typename _Tp, size_t _Nm>
> +    struct array;
> +
>    template<size_t...>
>      struct _Index_tuple;
>  
> +  template<size_t _Int, class _Tp1, class _Tp2>
> +    constexpr typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type&
> +    get(pair<_Tp1, _Tp2>& __in) noexcept;
> +
> +  template<size_t _Int, class _Tp1, class _Tp2>
> +    constexpr typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type&&
> +    get(pair<_Tp1, _Tp2>&& __in) noexcept;
> +
> +  template<size_t _Int, class _Tp1, class _Tp2>
> +    constexpr const typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type&
> +    get(const pair<_Tp1, _Tp2>& __in) noexcept;
> +
> +  template<size_t _Int, class _Tp1, class _Tp2>
> +    constexpr const typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type&&
> +    get(const pair<_Tp1, _Tp2>&& __in) noexcept;
> +
> +  template<size_t __i, typename... _Elements>
> +    constexpr __tuple_element_t<__i, tuple<_Elements...>>&
> +    get(tuple<_Elements...>& __t) noexcept;
> +
> +  template<size_t __i, typename... _Elements>
> +    constexpr const __tuple_element_t<__i, tuple<_Elements...>>&
> +    get(const tuple<_Elements...>& __t) noexcept;
> +
> +  template<size_t __i, typename... _Elements>
> +    constexpr __tuple_element_t<__i, tuple<_Elements...>>&&
> +    get(tuple<_Elements...>&& __t) noexcept;
> +
> +  template<size_t __i, typename... _Elements>
> +    constexpr const __tuple_element_t<__i, tuple<_Elements...>>&&
> +    get(const tuple<_Elements...>&& __t) noexcept;
> +
> +  template<size_t _Int, typename _Tp, size_t _Nm>
> +    constexpr _Tp&
> +    get(array<_Tp, _Nm>&) noexcept;
> +
> +  template<size_t _Int, typename _Tp, size_t _Nm>
> +    constexpr _Tp&&
> +    get(array<_Tp, _Nm>&&) noexcept;
> +
> +  template<size_t _Int, typename _Tp, size_t _Nm>
> +    constexpr const _Tp&
> +    get(const array<_Tp, _Nm>&) noexcept;
> +
> +  template<size_t _Int, typename _Tp, size_t _Nm>
> +    constexpr const _Tp&&
> +    get(const array<_Tp, _Nm>&&) noexcept;
> +
>  #if ! __cpp_lib_concepts
>    // Concept utility functions, reused in conditionally-explicit
>    // constructors.
> @@ -159,6 +217,46 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  #endif // lib concepts
>  #endif // C++11
>  
> +#if __glibcxx_tuple_like // >= C++23
> +  template<typename _Tp>
> +    inline constexpr bool __is_tuple_v = false;
> +
> +  template<typename... _Ts>
> +    inline constexpr bool __is_tuple_v<tuple<_Ts...>> = true;
> +
> +  // TODO: Reuse __is_tuple_like from <type_traits>?
> +  template<typename _Tp>
> +    inline constexpr bool __is_tuple_like_v = false;
> +
> +  template<typename... _Elements>
> +    inline constexpr bool __is_tuple_like_v<tuple<_Elements...>> = true;
> +
> +  template<typename _T1, typename _T2>
> +    inline constexpr bool __is_tuple_like_v<pair<_T1, _T2>> = true;
> +
> +  template<typename _Tp, size_t _Nm>
> +    inline constexpr bool __is_tuple_like_v<array<_Tp, _Nm>> = true;
> +
> +  // __is_tuple_like_v<subrange> is defined in <bits/ranges_util.h>.
> +
> +  template<typename _Tp>
> +    concept __tuple_like = __is_tuple_like_v<remove_cvref_t<_Tp>>;
> +
> +  template<typename _Tp>
> +    concept __pair_like = __tuple_like<_Tp> && 
> tuple_size_v<remove_cvref_t<_Tp>> == 2;
> +
> +  template<typename _Tp, typename _Tuple>
> +    concept __eligible_tuple_like
> +      = __detail::__different_from<_Tp, _Tuple> && __tuple_like<_Tp>
> +     && (tuple_size_v<remove_cvref_t<_Tp>> == tuple_size_v<_Tuple>)
> +     && !ranges::__detail::__is_subrange<remove_cvref_t<_Tp>>;
> +
> +  template<typename _Tp, typename _Pair>
> +    concept __eligible_pair_like
> +      = __detail::__different_from<_Tp, _Pair> && __pair_like<_Tp>
> +     && !ranges::__detail::__is_subrange<remove_cvref_t<_Tp>>;
> +#endif // C++23
> +
>    template<typename _U1, typename _U2> class __pair_base
>    {
>  #if __cplusplus >= 201103L && ! __cpp_lib_concepts
> @@ -295,6 +393,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         return false;
>  #endif
>       }
> +
> +#if __glibcxx_tuple_like // >= C++23
> +      template<typename _UPair>
> +     static constexpr bool
> +     _S_constructible_from_pair_like()
> +     {
> +       return _S_constructible<decltype(std::get<0>(std::declval<_UPair>())),
> +                               
> decltype(std::get<1>(std::declval<_UPair>()))>();
> +     }
> +
> +      template<typename _UPair>
> +     static constexpr bool
> +     _S_convertible_from_pair_like()
> +     {
> +       return _S_convertible<decltype(std::get<0>(std::declval<_UPair>())),
> +                             
> decltype(std::get<1>(std::declval<_UPair>()))>();
> +     }
> +
> +      template<typename _UPair>
> +     static constexpr bool
> +     _S_dangles_from_pair_like()
> +     {
> +       return _S_dangles<decltype(std::get<0>(std::declval<_UPair>())),
> +                         decltype(std::get<1>(std::declval<_UPair>()))>();
> +     }
> +#endif // C++23
>        /// @endcond
>  
>      public:
> @@ -393,6 +517,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       pair(const pair<_U1, _U2>&&) = delete;
>  #endif // C++23
>  
> +#if __glibcxx_tuple_like // >= C++23
> +      template<__eligible_pair_like<pair> _UPair>
> +     requires (_S_constructible_from_pair_like<_UPair>())
> +       && (!_S_dangles_from_pair_like<_UPair>())
> +     constexpr explicit(!_S_convertible_from_pair_like<_UPair>())
> +     pair(_UPair&& __p)
> +     : first(std::get<0>(std::forward<_UPair>(__p))),
> +       second(std::get<1>(std::forward<_UPair>(__p)))
> +     { }
> +
> +      template<__eligible_pair_like<pair> _UPair>
> +     requires (_S_constructible_from_pair_like<_UPair>())
> +       && (_S_dangles_from_pair_like<_UPair>())
> +     constexpr explicit(!_S_convertible_from_pair_like<_UPair>())
> +     pair(_UPair&&) = delete;
> +#endif // C++23
> +
>    private:
>        /// @cond undocumented
>        template<typename _U1, typename _U2>
> @@ -421,6 +562,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           return is_nothrow_assignable_v<_T2&, _U2>;
>         return false;
>       }
> +
> +#if __glibcxx_tuple_like // >= C++23
> +      template<typename _UPair>
> +     static constexpr bool
> +     _S_assignable_from_tuple_like()
> +     {
> +       return _S_assignable<decltype(std::get<0>(std::declval<_UPair>())),
> +                            decltype(std::get<1>(std::declval<_UPair>()))>();
> +     }
> +
> +      template<typename _UPair>
> +     static constexpr bool
> +     _S_const_assignable_from_tuple_like()
> +     {
> +       return 
> _S_const_assignable<decltype(std::get<0>(std::declval<_UPair>())),
> +                                  
> decltype(std::get<1>(std::declval<_UPair>()))>();
> +     }
> +#endif // C++23
>        /// @endcond
>  
>    public:
> @@ -516,6 +675,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         return *this;
>       }
>  #endif // C++23
> +
> +#if __glibcxx_tuple_like // >= C++23
> +      template<__eligible_pair_like<pair> _UPair>
> +     requires (_S_assignable_from_tuple_like<_UPair>())
> +     constexpr pair&
> +     operator=(_UPair&& __p)
> +     {
> +       first = std::get<0>(std::forward<_UPair>(__p));
> +       second = std::get<1>(std::forward<_UPair>(__p));
> +       return *this;
> +     }
> +
> +      template<__eligible_pair_like<pair> _UPair>
> +     requires (_S_const_assignable_from_tuple_like<_UPair>())
> +     constexpr const pair&
> +     operator=(_UPair&& __p) const
> +     {
> +       first = std::get<0>(std::forward<_UPair>(__p));
> +       second = std::get<1>(std::forward<_UPair>(__p));
> +       return *this;
> +     }
> +#endif // C++23
> +
>  #else // !__cpp_lib_concepts
>        // C++11/14/17 implementation using enable_if, partially constexpr.
>  
> diff --git a/libstdc++-v3/include/bits/utility.h 
> b/libstdc++-v3/include/bits/utility.h
> index d8a5fb960fe..2a741bf7000 100644
> --- a/libstdc++-v3/include/bits/utility.h
> +++ b/libstdc++-v3/include/bits/utility.h
> @@ -266,6 +266,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  #endif
>  #endif
>  
> +#if __glibcxx_ranges
> +  namespace ranges::__detail
> +  {
> +    template<typename _Range>
> +      inline constexpr bool __is_subrange = false;
> +  } // namespace __detail
> +#endif
> +
>  _GLIBCXX_END_NAMESPACE_VERSION
>  } // namespace
>  
> diff --git a/libstdc++-v3/include/bits/version.def 
> b/libstdc++-v3/include/bits/version.def
> index 8fb8a2877ee..502961eb269 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -1780,6 +1780,14 @@ ftms = {
>    };
>  };
>  
> +ftms = {
> +  name = tuple_like;
> +  values = {
> +    v = 202207;
> +    cxxmin = 23;
> +  };
> +};
> +
>  // Standard test specifications.
>  stds[97] = ">= 199711L";
>  stds[03] = ">= 199711L";
> diff --git a/libstdc++-v3/include/bits/version.h 
> b/libstdc++-v3/include/bits/version.h
> index 9ba99deeda6..511030bde47 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -2169,4 +2169,15 @@
>  #endif /* !defined(__cpp_lib_generator) && defined(__glibcxx_want_generator) 
> */
>  #undef __glibcxx_want_generator
>  
> +// from version.def line 1774
> +#if !defined(__cpp_lib_tuple_like)
> +# if (__cplusplus >= 202100L)
> +#  define __glibcxx_tuple_like 202207L
> +#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_tuple_like)
> +#   define __cpp_lib_tuple_like 202207L
> +#  endif
> +# endif
> +#endif /* !defined(__cpp_lib_tuple_like) && 
> defined(__glibcxx_want_tuple_like) */
> +#undef __glibcxx_want_tuple_like
> +
>  #undef __glibcxx_want_all
> diff --git a/libstdc++-v3/include/std/concepts 
> b/libstdc++-v3/include/std/concepts
> index 66ed3714b25..4f3e059b051 100644
> --- a/libstdc++-v3/include/std/concepts
> +++ b/libstdc++-v3/include/std/concepts
> @@ -62,6 +62,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      concept same_as
>        = __detail::__same_as<_Tp, _Up> && __detail::__same_as<_Up, _Tp>;
>  
> +  namespace __detail
> +  {
> +    template<typename _Tp, typename _Up>
> +      concept __different_from
> +     = !same_as<remove_cvref_t<_Tp>, remove_cvref_t<_Up>>;
> +  } // namespace __detail
> +
>    /// [concept.derived], concept derived_from
>    template<typename _Derived, typename _Base>
>      concept derived_from = __is_base_of(_Base, _Derived)
> @@ -185,8 +192,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  
>        template<typename _Tp, typename _Up>
>       concept __adl_swap
> -       = (__detail::__class_or_enum<remove_reference_t<_Tp>>
> -         || __detail::__class_or_enum<remove_reference_t<_Up>>)
> +       = (std::__detail::__class_or_enum<remove_reference_t<_Tp>>
> +         || std::__detail::__class_or_enum<remove_reference_t<_Up>>)
>         && requires(_Tp&& __t, _Up&& __u) {
>           swap(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u));
>         };
> diff --git a/libstdc++-v3/include/std/map b/libstdc++-v3/include/std/map
> index dcfd222d173..4a96e59a5bc 100644
> --- a/libstdc++-v3/include/std/map
> +++ b/libstdc++-v3/include/std/map
> @@ -74,6 +74,7 @@
>  #define __glibcxx_want_map_try_emplace
>  #define __glibcxx_want_node_extract
>  #define __glibcxx_want_nonmember_container_access
> +#define __glibcxx_want_tuple_like
>  #include <bits/version.h>
>  
>  #if __cplusplus >= 201703L
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index f2413badd9c..7d739852677 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -2389,11 +2389,7 @@ namespace views::__adaptor
>       inline constexpr bool __is_basic_string_view<basic_string_view<_CharT, 
> _Traits>>
>         = true;
>  
> -      template<typename _Range>
> -     inline constexpr bool __is_subrange = false;
> -
> -      template<typename _Iter, typename _Sent, subrange_kind _Kind>
> -     inline constexpr bool __is_subrange<subrange<_Iter, _Sent, _Kind>> = 
> true;
> +      using ranges::__detail::__is_subrange;
>  
>        template<typename _Range>
>       inline constexpr bool __is_iota_view = false;
> @@ -4166,6 +4162,10 @@ namespace views::__adaptor
>  
>    namespace __detail
>    {
> +#if __cpp_lib_tuple_like // >= C++23
> +    template<typename _Tp, size_t _Nm>
> +    concept __has_tuple_element = __tuple_like<_Tp> && _Nm < 
> tuple_size_v<_Tp>;
> +#else
>      template<typename _Tp, size_t _Nm>
>      concept __has_tuple_element = requires(_Tp __t)
>        {
> @@ -4175,6 +4175,7 @@ namespace views::__adaptor
>       { std::get<_Nm>(__t) }
>         -> convertible_to<const tuple_element_t<_Nm, _Tp>&>;
>        };
> +#endif
>  
>      template<typename _Tp, size_t _Nm>
>        concept __returnable_element
> @@ -4559,23 +4560,12 @@ namespace views::__adaptor
>       || (!(bidirectional_range<_Rs> && ...) && (common_range<_Rs> && ...))
>       || ((random_access_range<_Rs> && ...) && (sized_range<_Rs> && ...));
>  
> -    template<typename... _Ts>
> -      struct __tuple_or_pair
> -      { using type = std::tuple<_Ts...>; };
> -
> -    template<typename _Tp, typename _Up>
> -      struct __tuple_or_pair<_Tp, _Up>
> -      { using type = pair<_Tp, _Up>; };
> -
> -    template<typename... _Ts>
> -      using __tuple_or_pair_t = typename __tuple_or_pair<_Ts...>::type;
> -
>      template<typename _Fp, typename _Tuple>
>        constexpr auto
>        __tuple_transform(_Fp&& __f, _Tuple&& __tuple)
>        {
>       return std::apply([&]<typename... _Ts>(_Ts&&... __elts) {
> -       return __tuple_or_pair_t<invoke_result_t<_Fp&, _Ts>...>
> +       return tuple<invoke_result_t<_Fp&, _Ts>...>
>           (std::__invoke(__f, std::forward<_Ts>(__elts))...);
>       }, std::forward<_Tuple>(__tuple));
>        }
> @@ -4696,7 +4686,7 @@ namespace views::__adaptor
>  #ifdef __clang__ // LLVM-61763 workaround
>    public:
>  #endif
> -    __detail::__tuple_or_pair_t<iterator_t<__detail::__maybe_const_t<_Const, 
> _Vs>>...> _M_current;
> +    tuple<iterator_t<__detail::__maybe_const_t<_Const, _Vs>>...> _M_current;
>  
>      constexpr explicit
>      _Iterator(decltype(_M_current) __current)
> @@ -4728,7 +4718,7 @@ namespace views::__adaptor
>      // iterator_category defined in __zip_view_iter_cat
>      using iterator_concept = decltype(_S_iter_concept());
>      using value_type
> -      = 
> __detail::__tuple_or_pair_t<range_value_t<__detail::__maybe_const_t<_Const, 
> _Vs>>...>;
> +      = tuple<range_value_t<__detail::__maybe_const_t<_Const, _Vs>>...>;
>      using difference_type
>        = common_type_t<range_difference_t<__detail::__maybe_const_t<_Const, 
> _Vs>>...>;
>  
> @@ -4900,7 +4890,7 @@ namespace views::__adaptor
>    template<bool _Const>
>    class zip_view<_Vs...>::_Sentinel
>    {
> -    __detail::__tuple_or_pair_t<sentinel_t<__detail::__maybe_const_t<_Const, 
> _Vs>>...> _M_end;
> +    tuple<sentinel_t<__detail::__maybe_const_t<_Const, _Vs>>...> _M_end;
>  
>      constexpr explicit
>      _Sentinel(decltype(_M_end) __end)
> @@ -8325,8 +8315,7 @@ namespace views::__adaptor
>                   && __detail::__cartesian_product_is_common<_First, _Vs...>)
>      {
>        auto __its = [this]<size_t... _Is>(index_sequence<_Is...>) {
> -     using _Ret = __detail::__tuple_or_pair_t<iterator_t<_First>,
> -                                              iterator_t<_Vs>...>;
> +     using _Ret = tuple<iterator_t<_First>, iterator_t<_Vs>...>;
>       bool __empty_tail = (ranges::empty(std::get<1 + _Is>(_M_bases)) || ...);
>       auto& __first = std::get<0>(_M_bases);
>       return _Ret{(__empty_tail
> @@ -8342,8 +8331,7 @@ namespace views::__adaptor
>      end() const requires __detail::__cartesian_product_is_common<const 
> _First, const _Vs...>
>      {
>        auto __its = [this]<size_t... _Is>(index_sequence<_Is...>) {
> -     using _Ret = __detail::__tuple_or_pair_t<iterator_t<const _First>,
> -                                              iterator_t<const _Vs>...>;
> +     using _Ret = tuple<iterator_t<const _First>, iterator_t<const _Vs>...>;
>       bool __empty_tail = (ranges::empty(std::get<1 + _Is>(_M_bases)) || ...);
>       auto& __first = std::get<0>(_M_bases);
>       return _Ret{(__empty_tail
> @@ -8416,8 +8404,8 @@ namespace views::__adaptor
>    {
>      using _Parent = __maybe_const_t<_Const, cartesian_product_view>;
>      _Parent* _M_parent = nullptr;
> -    __detail::__tuple_or_pair_t<iterator_t<__maybe_const_t<_Const, _First>>,
> -                             iterator_t<__maybe_const_t<_Const, _Vs>>...> 
> _M_current;
> +    tuple<iterator_t<__maybe_const_t<_Const, _First>>,
> +       iterator_t<__maybe_const_t<_Const, _Vs>>...> _M_current;
>  
>      constexpr
>      _Iterator(_Parent& __parent, decltype(_M_current) __current)
> @@ -8444,11 +8432,11 @@ namespace views::__adaptor
>      using iterator_category = input_iterator_tag;
>      using iterator_concept = decltype(_S_iter_concept());
>      using value_type
> -      = __detail::__tuple_or_pair_t<range_value_t<__maybe_const_t<_Const, 
> _First>>,
> -                                 range_value_t<__maybe_const_t<_Const, 
> _Vs>>...>;
> +      = tuple<range_value_t<__maybe_const_t<_Const, _First>>,
> +           range_value_t<__maybe_const_t<_Const, _Vs>>...>;
>      using reference
> -      = 
> __detail::__tuple_or_pair_t<range_reference_t<__maybe_const_t<_Const, 
> _First>>,
> -                                 range_reference_t<__maybe_const_t<_Const, 
> _Vs>>...>;
> +      = tuple<range_reference_t<__maybe_const_t<_Const, _First>>,
> +           range_reference_t<__maybe_const_t<_Const, _Vs>>...>;
>      using difference_type = 
> decltype(cartesian_product_view::_S_difference_type());
>  
>      _Iterator() = default;
> diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
> index be92f1eb973..ba364d6a4f8 100644
> --- a/libstdc++-v3/include/std/tuple
> +++ b/libstdc++-v3/include/std/tuple
> @@ -50,6 +50,7 @@
>  #define __glibcxx_want_apply
>  #define __glibcxx_want_make_from_tuple
>  #define __glibcxx_want_ranges_zip
> +#define __glibcxx_want_tuple_like
>  #include <bits/version.h>
>  
>  namespace std _GLIBCXX_VISIBILITY(default)
> @@ -246,6 +247,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        _Head _M_head_impl;
>      };
>  
> +#if __cpp_lib_tuple_like // >= C++23
> +  struct __tuple_like_tag_t { explicit __tuple_like_tag_t() = default; };
> +
> +  // These forward declarations are used by the operator<=> overload for
> +  // tuple-like types.
> +  template<typename _Cat, typename _Tp, typename _Up>
> +    constexpr _Cat
> +    __tuple_cmp(const _Tp&, const _Up&, index_sequence<>);
> +
> +  template<typename _Cat, typename _Tp, typename _Up,
> +        size_t _Idx0, size_t... _Idxs>
> +    constexpr _Cat
> +    __tuple_cmp(const _Tp& __t, const _Up& __u,
> +             index_sequence<_Idx0, _Idxs...>);
> +#endif // C++23
> +
>    /**
>     * Contains the actual implementation of the @c tuple template, stored
>     * as a recursive inheritance hierarchy from the first element (most
> @@ -342,6 +359,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       { }
>  #endif // C++23
>  
> +#if __cpp_lib_tuple_like // >= C++23
> +      template<typename _UTuple, size_t... _Is>
> +     constexpr
> +     _Tuple_impl(__tuple_like_tag_t, _UTuple&& __u, index_sequence<_Is...>)
> +     : _Tuple_impl(std::get<_Is>(std::forward<_UTuple>(__u))...)
> +     { }
> +#endif // C++23
> +
>        template<typename _Alloc>
>       _GLIBCXX20_CONSTEXPR
>       _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a)
> @@ -428,6 +453,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       { }
>  #endif // C++23
>  
> +#if __cpp_lib_tuple_like // >= C++23
> +      template<typename _Alloc, typename _UTuple, size_t... _Is>
> +     constexpr
> +     _Tuple_impl(__tuple_like_tag_t, allocator_arg_t __tag, const _Alloc& 
> __a,
> +                 _UTuple&& __u, index_sequence<_Is...>)
> +     : _Tuple_impl(__tag, __a, std::get<_Is>(std::forward<_UTuple>(__u))...)
> +     { }
> +#endif // C++23
> +
>        template<typename... _UElements>
>       _GLIBCXX20_CONSTEXPR
>       void
> @@ -470,6 +504,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       }
>  #endif // C++23
>  
> +#if __cpp_lib_tuple_like // >= C++23
> +      template<typename _UTuple>
> +     constexpr void
> +     _M_assign(__tuple_like_tag_t __tag, _UTuple&& __u)
> +     {
> +       _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u));
> +       _M_tail(*this)._M_assign(__tag, std::forward<_UTuple>(__u));
> +     }
> +
> +      template<typename _UTuple>
> +     constexpr void
> +     _M_assign(__tuple_like_tag_t __tag, _UTuple&& __u) const
> +     {
> +       _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u));
> +       _M_tail(*this)._M_assign(__tag, std::forward<_UTuple>(__u));
> +     }
> +#endif // C++23
> +
>      protected:
>        _GLIBCXX20_CONSTEXPR
>        void
> @@ -563,6 +615,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       { }
>  #endif // C++23
>  
> +#if __cpp_lib_tuple_like // >= C++23
> +      template<typename _UTuple>
> +     constexpr
> +     _Tuple_impl(__tuple_like_tag_t, _UTuple&& __u, index_sequence<0>)
> +     : _Tuple_impl(std::get<0>(std::forward<_UTuple>(__u)))
> +     { }
> +#endif // C++23
> +
>        template<typename _Alloc>
>       _GLIBCXX20_CONSTEXPR
>       _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a)
> @@ -633,6 +693,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       { }
>  #endif // C++23
>  
> +#if __cpp_lib_tuple_like // >= C++23
> +      template<typename _Alloc, typename _UTuple>
> +     constexpr
> +     _Tuple_impl(__tuple_like_tag_t, allocator_arg_t __tag, const _Alloc& 
> __a,
> +                 _UTuple&& __u, index_sequence<0>)
> +     : _Tuple_impl(__tag, __a, std::get<0>(std::forward<_UTuple>(__u)))
> +     { }
> +#endif // C++23
> +
>        template<typename _UHead>
>       _GLIBCXX20_CONSTEXPR
>       void
> @@ -667,6 +736,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       }
>  #endif // C++23
>  
> +#if __cpp_lib_tuple_like // >= C++23
> +    template<typename _UTuple>
> +      constexpr void
> +      _M_assign(__tuple_like_tag_t, _UTuple&& __u)
> +      { _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); }
> +
> +    template<typename _UTuple>
> +      constexpr void
> +      _M_assign(__tuple_like_tag_t, _UTuple&& __u) const
> +      { _M_head(*this) = std::get<_Idx>(std::forward<_UTuple>(__u)); }
> +#endif // C++23
> +
>      protected:
>        _GLIBCXX20_CONSTEXPR
>        void
> @@ -846,6 +927,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  #endif
>       }
>  
> +#if __cpp_lib_tuple_like // >= C++23
> +      template<typename _UTuple>
> +     static consteval bool
> +     __dangles_from_tuple_like()
> +     {
> +       return []<size_t... _Is>(index_sequence<_Is...>) {
> +         return 
> __dangles<decltype(std::get<_Is>(std::declval<_UTuple>()))...>();
> +       }(index_sequence_for<_Elements...>{});
> +     }
> +
> +      template<typename _UTuple>
> +     static consteval bool
> +     __constructible_from_tuple_like()
> +     {
> +       return []<size_t... _Is>(index_sequence<_Is...>) {
> +         return 
> __constructible<decltype(std::get<_Is>(std::declval<_UTuple>()))...>();
> +       }(index_sequence_for<_Elements...>{});
> +     }
> +
> +      template<typename _UTuple>
> +     static consteval bool
> +     __convertible_from_tuple_like()
> +     {
> +       return []<size_t... _Is>(index_sequence<_Is...>) {
> +         return 
> __convertible<decltype(std::get<_Is>(std::declval<_UTuple>()))...>();
> +       }(index_sequence_for<_Elements...>{});
> +     }
> +#endif // C++23
> +
>      public:
>        constexpr
>        explicit(!(__is_implicitly_default_constructible_v<_Elements> && ...))
> @@ -1016,10 +1126,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(const pair<_U1, _U2>&&) = delete;
>  #endif // C++23
>  
> -#if 0 && __cpp_lib_tuple_like // >= C++23
> -      template<__tuple_like _UTuple>
> -     constexpr explicit(...)
> -     tuple(_UTuple&& __u);
> +#if __cpp_lib_tuple_like // >= C++23
> +      template<__eligible_tuple_like<tuple> _UTuple>
> +     requires (__constructible_from_tuple_like<_UTuple>())
> +       && (!__use_other_ctor<_UTuple>())
> +       && (!__dangles_from_tuple_like<_UTuple>())
> +     constexpr explicit(!__convertible_from_tuple_like<_UTuple>())
> +     tuple(_UTuple&& __u)
> +     : _Inherited(__tuple_like_tag_t{},
> +                  std::forward<_UTuple>(__u),
> +                  index_sequence_for<_Elements...>{})
> +     { }
> +
> +      template<__eligible_tuple_like<tuple> _UTuple>
> +     requires (__constructible_from_tuple_like<_UTuple>())
> +       && (!__use_other_ctor<_UTuple>())
> +       && (__dangles_from_tuple_like<_UTuple>())
> +     tuple(_UTuple&&) = delete;
>  #endif // C++23
>  
>        // Allocator-extended constructors.
> @@ -1202,10 +1325,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
>  #endif // C++23
>  
> -#if 0 && __cpp_lib_tuple_like // >= C++23
> -      template<typename _Alloc, __tuple_like _UTuple>
> -     constexpr explicit(...)
> -     tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& __u);
> +#if __cpp_lib_tuple_like // >= C++23
> +      template<typename _Alloc, __eligible_tuple_like<tuple> _UTuple>
> +     requires (__constructible_from_tuple_like<_UTuple>())
> +       && (!__use_other_ctor<_UTuple>())
> +       && (!__dangles_from_tuple_like<_UTuple>())
> +     constexpr explicit(!__convertible_from_tuple_like<_UTuple>())
> +     tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& __u)
> +     : _Inherited(__tuple_like_tag_t{},
> +                  __tag, __a, std::forward<_UTuple>(__u),
> +                  index_sequence_for<_Elements...>{})
> +     { }
> +
> +      template<typename _Alloc, __eligible_tuple_like<tuple> _UTuple>
> +     requires (__constructible_from_tuple_like<_UTuple>())
> +       && (!__use_other_ctor<_UTuple>())
> +       && (__dangles_from_tuple_like<_UTuple>())
> +     tuple(allocator_arg_t, const _Alloc&, _UTuple&&) = delete;
>  #endif // C++23
>  
>  #else // !(concepts && conditional_explicit)
> @@ -1539,6 +1675,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       }
>  #endif // C++23
>  
> +#if __cpp_lib_tuple_like // >= C++23
> +      template<typename _UTuple>
> +     static consteval bool
> +     __assignable_from_tuple_like()
> +     {
> +       return []<size_t... _Is>(index_sequence<_Is...>) {
> +         return 
> __assignable<decltype(std::get<_Is>(std::declval<_UTuple>()))...>();
> +       }(index_sequence_for<_Elements...>{});
> +     }
> +
> +      template<typename _UTuple>
> +     static consteval bool
> +     __const_assignable_from_tuple_like()
> +     {
> +       return []<size_t... _Is>(index_sequence<_Is...>) {
> +         return 
> __const_assignable<decltype(std::get<_Is>(std::declval<_UTuple>()))...>();
> +       }(index_sequence_for<_Elements...>{});
> +     }
> +#endif // C++23
> +
>      public:
>  
>        tuple& operator=(const tuple& __u) = delete;
> @@ -1661,14 +1817,59 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       }
>  #endif // C++23
>  
> -#if 0 && __cpp_lib_tuple_like // >= C++23
> -      template<__tuple_like _UTuple>
> +#if __cpp_lib_tuple_like // >= C++23
> +      template<__eligible_tuple_like<tuple> _UTuple>
> +     requires (__assignable_from_tuple_like<_UTuple>())
>       constexpr tuple&
> -     operator=(_UTuple&& __u);
> +     operator=(_UTuple&& __u)
> +     {
> +       this->_M_assign(__tuple_like_tag_t{}, std::forward<_UTuple>(__u));
> +       return *this;
> +     }
> +
> +      template<__eligible_tuple_like<tuple> _UTuple>
> +     requires (__const_assignable_from_tuple_like<_UTuple>())
> +     constexpr const tuple&
> +     operator=(_UTuple&& __u) const
> +     {
> +       this->_M_assign(__tuple_like_tag_t{}, std::forward<_UTuple>(__u));
> +       return *this;
> +     }
>  
>        template<__tuple_like _UTuple>
> -     constexpr tuple&
> -     operator=(_UTuple&& __u) const;
> +     requires (!__is_tuple_v<_UTuple>)
> +     friend constexpr bool
> +     operator==(const tuple& __t, const _UTuple& __u)
> +     {
> +       static_assert(sizeof...(_Elements) == tuple_size_v<_UTuple>,
> +           "tuple objects can only be compared if they have equal sizes.");
> +       return [&]<size_t... _Is>(index_sequence<_Is...>) {
> +         return (bool(std::get<_Is>(__t) == std::get<_Is>(__u))
> +                 && ...);
> +       }(index_sequence_for<_Elements...>{});
> +     }
> +
> +      template<__tuple_like _UTuple,
> +            typename = make_index_sequence<tuple_size_v<_UTuple>>>
> +     struct __tuple_like_common_comparison_category;
> +
> +      template<__tuple_like _UTuple, size_t... _Is>
> +     requires requires
> +       { typename void_t<__detail::__synth3way_t<_Elements, 
> tuple_element_t<_Is, _UTuple>>...>; }
> +     struct __tuple_like_common_comparison_category<_UTuple, 
> index_sequence<_Is...>>
> +     {
> +       using type = common_comparison_category_t
> +         <__detail::__synth3way_t<_Elements, tuple_element_t<_Is, 
> _UTuple>>...>;
> +     };
> +
> +      template<__tuple_like _UTuple>
> +     requires (!__is_tuple_v<_UTuple>)
> +     friend constexpr typename 
> __tuple_like_common_comparison_category<_UTuple>::type
> +     operator<=>(const tuple& __t, const _UTuple& __u)
> +     {
> +       using _Cat = typename 
> __tuple_like_common_comparison_category<_UTuple>::type;
> +       return std::__tuple_cmp<_Cat>(__t, __u, 
> index_sequence_for<_Elements...>());
> +     }
>  #endif // C++23
>  
>  #else // ! (concepts && consteval)
> @@ -2433,27 +2634,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      forward_as_tuple(_Elements&&... __args) noexcept
>      { return tuple<_Elements&&...>(std::forward<_Elements>(__args)...); }
>  
> -  // Declarations of std::array and its std::get overloads, so that
> -  // std::tuple_cat can use them if <tuple> is included before <array>.
> -
> -  template<typename _Tp, size_t _Nm> struct array;
> -
> -  template<size_t _Int, typename _Tp, size_t _Nm>
> -    constexpr _Tp&
> -    get(array<_Tp, _Nm>&) noexcept;
> -
> -  template<size_t _Int, typename _Tp, size_t _Nm>
> -    constexpr _Tp&&
> -    get(array<_Tp, _Nm>&&) noexcept;
> -
> -  template<size_t _Int, typename _Tp, size_t _Nm>
> -    constexpr const _Tp&
> -    get(const array<_Tp, _Nm>&) noexcept;
> -
> -  template<size_t _Int, typename _Tp, size_t _Nm>
> -    constexpr const _Tp&&
> -    get(const array<_Tp, _Nm>&&) noexcept;
> -
>    /// @cond undocumented
>    template<size_t, typename, typename, size_t>
>      struct __make_tuple_impl;
> @@ -2569,8 +2749,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    /// @endcond
>  
>    /// Create a `tuple` containing all elements from multiple tuple-like 
> objects
> +#if __cpp_lib_tuple_like // >= C++23
> +  template<__tuple_like... _Tpls>
> +#else
>    template<typename... _Tpls, typename = typename
>             enable_if<__and_<__is_tuple_like<_Tpls>...>::value>::type>
> +#endif
>      constexpr auto
>      tuple_cat(_Tpls&&... __tpls)
>      -> typename __tuple_cat_result<_Tpls...>::__type
> @@ -2722,7 +2906,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>                          std::get<_Idx>(std::forward<_Tuple>(__t))...);
>      }
>  
> +#if __cpp_lib_tuple_like // >= C++23
> +  template <typename _Fn, __tuple_like _Tuple>
> +#else
>    template <typename _Fn, typename _Tuple>
> +#endif
>      constexpr decltype(auto)
>      apply(_Fn&& __f, _Tuple&& __t)
>      noexcept(__unpack_std_tuple<is_nothrow_invocable, _Fn, _Tuple>)
> @@ -2741,7 +2929,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      __make_from_tuple_impl(_Tuple&& __t, index_sequence<_Idx...>)
>      { return _Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...); }
>  
> +#if __cpp_lib_tuple_like // >= C++23
> +  template <typename _Tp, __tuple_like _Tuple>
> +#else
>    template <typename _Tp, typename _Tuple>
> +#endif
>      constexpr _Tp
>      make_from_tuple(_Tuple&& __t)
>      noexcept(__unpack_std_tuple<is_nothrow_constructible, _Tp, _Tuple>)
> @@ -2759,17 +2951,60 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      }
>  #endif
>  
> -#if __cpp_lib_ranges_zip // >= C++23
> -  template<typename... _TTypes, typename... _UTypes,
> +#if __cpp_lib_tuple_like // >= C++23
> +  template<__tuple_like _TTuple, __tuple_like _UTuple,
> +        template<typename> class _TQual, template<typename> class _UQual,
> +        typename = make_index_sequence<tuple_size_v<_TTuple>>>
> +  struct __tuple_like_common_reference;
> +
> +  template<__tuple_like _TTuple, __tuple_like _UTuple,
> +        template<typename> class _TQual, template<typename> class _UQual,
> +        size_t... _Is>
> +    requires requires
> +      { typename tuple<common_reference_t<_TQual<tuple_element_t<_Is, 
> _TTuple>>,
> +                                       _UQual<tuple_element_t<_Is, 
> _UTuple>>>...>; }
> +  struct __tuple_like_common_reference<_TTuple, _UTuple, _TQual, _UQual, 
> index_sequence<_Is...>>
> +  {
> +    using type = tuple<common_reference_t<_TQual<tuple_element_t<_Is, 
> _TTuple>>,
> +                                       _UQual<tuple_element_t<_Is, 
> _UTuple>>>...>;
> +  };
> +
> +  template<__tuple_like _TTuple, __tuple_like _UTuple,
>          template<typename> class _TQual, template<typename> class _UQual>
> -    requires requires { typename tuple<common_reference_t<_TQual<_TTypes>, 
> _UQual<_UTypes>>...>; }
> -  struct basic_common_reference<tuple<_TTypes...>, tuple<_UTypes...>, 
> _TQual, _UQual>
> -  { using type = tuple<common_reference_t<_TQual<_TTypes>, 
> _UQual<_UTypes>>...>; };
> -
> -  template<typename... _TTypes, typename... _UTypes>
> -    requires requires { typename tuple<common_type_t<_TTypes, _UTypes>...>; }
> -  struct common_type<tuple<_TTypes...>, tuple<_UTypes...>>
> -  { using type = tuple<common_type_t<_TTypes, _UTypes>...>; };
> +    requires (__is_tuple_v<_TTuple> || __is_tuple_v<_UTuple>)
> +      && is_same_v<_TTuple, decay_t<_TTuple>>
> +      && is_same_v<_UTuple, decay_t<_UTuple>>
> +      && (tuple_size_v<_TTuple> == tuple_size_v<_UTuple>)
> +      && requires { typename __tuple_like_common_reference<_TTuple, _UTuple, 
> _TQual, _UQual>::type; }
> +  struct basic_common_reference<_TTuple, _UTuple, _TQual, _UQual>
> +  {
> +    using type = typename __tuple_like_common_reference<_TTuple, _UTuple, 
> _TQual, _UQual>::type;
> +  };
> +
> +  template<__tuple_like _TTuple, __tuple_like _UTuple,
> +        typename = make_index_sequence<tuple_size_v<_TTuple>>>
> +  struct __tuple_like_common_type;
> +
> +  template<__tuple_like _TTuple, __tuple_like _UTuple, size_t... _Is>
> +    requires requires
> +      { typename tuple<common_type_t<tuple_element_t<_Is, _TTuple>,
> +                                tuple_element_t<_Is, _UTuple>>...>; }
> +  struct __tuple_like_common_type<_TTuple, _UTuple, index_sequence<_Is...>>
> +  {
> +    using type = tuple<common_type_t<tuple_element_t<_Is, _TTuple>,
> +                                  tuple_element_t<_Is, _UTuple>>...>;
> +  };
> +
> +  template<__tuple_like _TTuple, __tuple_like _UTuple>
> +    requires (__is_tuple_v<_TTuple> || __is_tuple_v<_UTuple>)
> +      && is_same_v<_TTuple, decay_t<_TTuple>>
> +      && is_same_v<_UTuple, decay_t<_UTuple>>
> +      && (tuple_size_v<_TTuple> == tuple_size_v<_UTuple>)
> +      && requires { typename __tuple_like_common_type<_TTuple, 
> _UTuple>::type; }
> +  struct common_type<_TTuple, _UTuple>
> +  {
> +    using type = typename __tuple_like_common_type<_TTuple, _UTuple>::type;
> +  };
>  #endif // C++23
>  
>    /// @}
> diff --git a/libstdc++-v3/include/std/unordered_map 
> b/libstdc++-v3/include/std/unordered_map
> index efad0cef584..ea6129d6494 100644
> --- a/libstdc++-v3/include/std/unordered_map
> +++ b/libstdc++-v3/include/std/unordered_map
> @@ -51,6 +51,7 @@
>  #define __glibcxx_want_node_extract
>  #define __glibcxx_want_nonmember_container_access
>  #define __glibcxx_want_unordered_map_try_emplace
> +#define __glibcxx_want_tuple_like
>  #include <bits/version.h>
>  
>  #if __cplusplus >= 201703L
> diff --git a/libstdc++-v3/include/std/utility 
> b/libstdc++-v3/include/std/utility
> index f113d572e59..212513f6f48 100644
> --- a/libstdc++-v3/include/std/utility
> +++ b/libstdc++-v3/include/std/utility
> @@ -92,6 +92,7 @@
>  #define __glibcxx_want_tuple_element_t
>  #define __glibcxx_want_tuples_by_type
>  #define __glibcxx_want_unreachable
> +#define __glibcxx_want_tuple_like
>  #include <bits/version.h>
>  
>  namespace std _GLIBCXX_VISIBILITY(default)
> diff --git a/libstdc++-v3/testsuite/20_util/pair/p2165r4.cc 
> b/libstdc++-v3/testsuite/20_util/pair/p2165r4.cc
> new file mode 100644
> index 00000000000..ef06df1c53f
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/pair/p2165r4.cc
> @@ -0,0 +1,173 @@
> +// Verify P2165R4 enhancements to std::pair.
> +// { dg-do run { target c++23 } }
> +
> +#include <array>
> +#include <tuple>
> +#include <utility>
> +#include <testsuite_hooks.h>
> +
> +using std::array;
> +using std::pair;
> +using std::tuple;
> +
> +struct A { };
> +
> +template<template<typename> class pair_like_t>
> +constexpr bool
> +test01()
> +{
> +  struct B {
> +    int m;
> +    constexpr B(A&) : m(0) { }
> +    constexpr B(A&&) : m(1) { }
> +    constexpr B(const A&) : m(2) { }
> +    constexpr B(const A&&) : m(3) { }
> +  };
> +
> +  // template<pair-like UPair>
> +  //   constexpr explicit(false) pair(UPair&&);
> +
> +  pair_like_t<A> pair_like;
> +
> +  [&] {
> +    pair<B, B> p2b = pair_like;
> +    VERIFY( p2b.first.m == 0 && p2b.second.m == 0 );
> +  }();
> +  [&] {
> +    pair<B, B> p2b = std::move(pair_like);
> +    VERIFY( p2b.first.m == 1 && p2b.second.m == 1 );
> +  }();
> +  [&] {
> +    pair<B, B> p2b = std::as_const(pair_like);
> +    VERIFY( p2b.first.m == 2 && p2b.second.m == 2 );
> +  }();
> +  [&] {
> +    pair<B, B> p2b = std::move(std::as_const(pair_like));
> +    VERIFY( p2b.first.m == 3 && p2b.second.m == 3 );
> +  }();
> +
> +  // Verify dangling checks.
> +  static_assert( !std::is_constructible_v<pair<const int&, int>, 
> pair_like_t<long>> );
> +  static_assert( !std::is_constructible_v<pair<int, const int&>, 
> pair_like_t<long>> );
> +
> +  return true;
> +}
> +
> +template<template<typename> class pair_like_t>
> +constexpr bool
> +test02()
> +{
> +  struct B {
> +    int m;
> +    constexpr explicit B(A&) : m(0) { }
> +    constexpr explicit B(A&&) : m(1) { }
> +    constexpr explicit B(const A&) : m(2) { }
> +    constexpr explicit B(const A&&) : m(3) { }
> +  };
> +
> +  // template<pair-like UPair>
> +  //   constexpr explicit(true) pair(UPair&&);
> +
> +  static_assert( !std::is_convertible_v<pair_like_t<A>, pair<B, B>> );
> +
> +  pair_like_t<A> pair_like;
> +
> +  [&] {
> +    pair<B, B> p2b{pair_like};
> +    VERIFY( p2b.first.m == 0 && p2b.second.m == 0 );
> +  }();
> +  [&] {
> +    pair<B, B> p2b{std::move(pair_like)};
> +    VERIFY( p2b.first.m == 1 && p2b.second.m == 1 );
> +  }();
> +  [&] {
> +    pair<B, B> p2b{std::as_const(pair_like)};
> +    VERIFY( p2b.first.m == 2 && p2b.second.m == 2 );
> +  }();
> +  [&] {
> +    pair<B, B> p2b{std::move(std::as_const(pair_like))};
> +    VERIFY( p2b.first.m == 3 && p2b.second.m == 3 );
> +  }();
> +
> +  return true;
> +}
> +
> +template<template<typename> class pair_like_t>
> +constexpr bool
> +test03()
> +{
> +  struct B {
> +    int m;
> +    constexpr B& operator=(A&) { m = 0; return *this; }
> +    constexpr B& operator=(A&&) { m = 1; return *this; }
> +    constexpr B& operator=(const A&) { m = 2; return *this; }
> +    constexpr B& operator=(const A&&) { m = 3; return *this; }
> +  };
> +
> +  // template<pair-like UPair>
> +  //   constexpr pair& operator=(UPair&&);
> +
> +  pair_like_t<A> pair_like;
> +
> +  pair<B, B> p2b;
> +  p2b = pair_like;
> +  VERIFY( p2b.first.m == 0 && p2b.second.m == 0 );
> +  p2b = std::move(pair_like);
> +  VERIFY( p2b.first.m == 1 && p2b.second.m == 1 );
> +  p2b = std::as_const(pair_like);
> +  VERIFY( p2b.first.m == 2 && p2b.second.m == 2 );
> +  p2b = std::move(std::as_const(pair_like));
> +  VERIFY( p2b.first.m == 3 && p2b.second.m == 3 );
> +
> +  return true;
> +}
> +
> +template<template<typename> class pair_like_t>
> +constexpr bool
> +test04()
> +{
> +  struct B {
> +    mutable int m;
> +    constexpr const B& operator=(A&) const { m = 0; return *this; }
> +    constexpr const B& operator=(A&&) const { m = 1; return *this; }
> +    constexpr const B& operator=(const A&) const { m = 2; return *this; }
> +    constexpr const B& operator=(const A&&) const { m = 3; return *this; }
> +  };
> +
> +  // template<pair-like UPair>
> +  //   constexpr const pair& operator=(UPair&&) const;
> +
> +  pair_like_t<A> pair_like;
> +
> +  const pair<B, B> p2b;
> +  p2b = pair_like;
> +  VERIFY( p2b.first.m == 0 && p2b.second.m == 0 );
> +  p2b = std::move(pair_like);
> +  VERIFY( p2b.first.m == 1 && p2b.second.m == 1 );
> +  p2b = std::as_const(pair_like);
> +  VERIFY( p2b.first.m == 2 && p2b.second.m == 2 );
> +  p2b = std::move(std::as_const(pair_like));
> +  VERIFY( p2b.first.m == 3 && p2b.second.m == 3 );
> +
> +  return true;
> +}
> +
> +template<typename T>
> +using pair_like_array = array<T, 2>;
> +
> +template<typename T>
> +using pair_like_tuple = tuple<T, T>;
> +
> +int
> +main()
> +{
> +  static_assert( test01<pair_like_array>() );
> +  static_assert( test02<pair_like_array>() );
> +  static_assert( test03<pair_like_array>() );
> +  static_assert( test04<pair_like_array>() );
> +
> +  static_assert( test01<pair_like_tuple>() );
> +  static_assert( test02<pair_like_tuple>() );
> +  static_assert( test03<pair_like_tuple>() );
> +  static_assert( test04<pair_like_tuple>() );
> +}
> diff --git a/libstdc++-v3/testsuite/20_util/tuple/p2165r4.cc 
> b/libstdc++-v3/testsuite/20_util/tuple/p2165r4.cc
> new file mode 100644
> index 00000000000..e2437c469b6
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/tuple/p2165r4.cc
> @@ -0,0 +1,335 @@
> +// Verify P2165R4 enhancements to std::tuple.
> +// { dg-do run { target c++23 } }
> +
> +#include <array>
> +#include <tuple>
> +#include <utility>
> +#include <memory>
> +#include <testsuite_hooks.h>
> +
> +using std::array;
> +using std::pair;
> +using std::tuple;
> +using std::allocator;
> +using std::allocator_arg_t;
> +using std::allocator_arg;
> +
> +namespace alloc {
> +  struct B01;
> +  struct B02;
> +}
> +
> +template<> struct std::uses_allocator<alloc::B01, allocator<int>> : 
> std::true_type { };
> +template<> struct std::uses_allocator<alloc::B02, allocator<int>> : 
> std::true_type { };
> +
> +struct A { };
> +
> +template<template<typename> class tuple_like_t>
> +constexpr bool
> +test01()
> +{
> +  struct B {
> +    int m;
> +    constexpr B(A&) : m(0) { }
> +    constexpr B(A&&) : m(1) { }
> +    constexpr B(const A&) : m(2) { }
> +    constexpr B(const A&&) : m(3) { }
> +  };
> +
> +  // template<tuple-like UTuple>
> +  //   constexpr explicit(false) tuple(UTuple&&);
> +
> +  tuple_like_t<A> tuple_like;
> +
> +  [&] {
> +    tuple<B, B, B> t3b = tuple_like;
> +    VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && 
> std::get<2>(t3b).m == 0 );
> +  }();
> +  [&] {
> +    tuple<B, B, B> t3b = std::move(tuple_like);
> +    VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && 
> std::get<2>(t3b).m == 1 );
> +  }();
> +  [&] {
> +    tuple<B, B, B> t3b = std::as_const(tuple_like);
> +    VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && 
> std::get<2>(t3b).m == 2 );
> +  }();
> +  [&] {
> +    tuple<B, B, B> t3b = std::move(std::as_const(tuple_like));
> +    VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && 
> std::get<2>(t3b).m == 3 );
> +  }();
> +
> +  // Verify dangling checks.
> +  static_assert( !std::is_constructible_v<tuple<const int&, int, int>, 
> tuple_like_t<long>> );
> +  static_assert( !std::is_constructible_v<tuple<int, const int&, int>, 
> tuple_like_t<long>> );
> +  static_assert( !std::is_constructible_v<tuple<int, int, const int&>, 
> tuple_like_t<long>> );
> +
> +  return true;
> +}
> +
> +namespace alloc
> +{
> +  struct B01 {
> +    int m;
> +    B01(A&);
> +    B01(A&&);
> +    B01(const A&);
> +    B01(const A&&);
> +    constexpr B01(allocator_arg_t, allocator<int>, A&) : m(0) { }
> +    constexpr B01(allocator_arg_t, allocator<int>, A&&) : m(1) { }
> +    constexpr B01(allocator_arg_t, allocator<int>, const A&) : m(2) { }
> +    constexpr B01(allocator_arg_t, allocator<int>, const A&&) : m(3) { }
> +  };
> +
> +  template<template<typename> class tuple_like_t>
> +  constexpr bool
> +  test01()
> +  {
> +    using B = B01;
> +
> +    // template<tuple-like UTuple>
> +    //   constexpr explicit(false) tuple(allocator_arg_t, const Alloc&, 
> UTuple&&);
> +
> +    tuple_like_t<A> tuple_like;
> +
> +    [&] {
> +      tuple<B, B, B> t3b = {allocator_arg, allocator<int>{}, tuple_like};
> +      VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && 
> std::get<2>(t3b).m == 0 );
> +    }();
> +    [&] {
> +      tuple<B, B, B> t3b = {allocator_arg, allocator<int>{}, 
> std::move(tuple_like)};
> +      VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && 
> std::get<2>(t3b).m == 1 );
> +    }();
> +    [&] {
> +      tuple<B, B, B> t3b = {allocator_arg, allocator<int>{}, 
> std::as_const(tuple_like)};
> +      VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && 
> std::get<2>(t3b).m == 2 );
> +    }();
> +    [&] {
> +      tuple<B, B, B> t3b = {allocator_arg, allocator<int>{}, 
> std::move(std::as_const(tuple_like))};
> +      VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && 
> std::get<2>(t3b).m == 3 );
> +    }();
> +
> +  // Verify dangling checks.
> +    static_assert( !std::is_constructible_v<tuple<const int&, int, int>,
> +                                         allocator_arg_t, allocator<int>,
> +                                         tuple_like_t<long>> );
> +    static_assert( !std::is_constructible_v<tuple<int, const int&, int>,
> +                                         allocator_arg_t, allocator<int>,
> +                                         tuple_like_t<long>> );
> +    static_assert( !std::is_constructible_v<tuple<int, int, const int&>,
> +                                         allocator_arg_t, allocator<int>,
> +                                         tuple_like_t<long>> );
> +
> +    return true;
> +  }
> +}
> +
> +template<template<typename> class tuple_like_t>
> +constexpr bool
> +test02()
> +{
> +  struct B {
> +    int m;
> +    constexpr explicit B(A&) : m(0) { }
> +    constexpr explicit B(A&&) : m(1) { }
> +    constexpr explicit B(const A&) : m(2) { }
> +    constexpr explicit B(const A&&) : m(3) { }
> +  };
> +
> +  // template<tuple-like UTuple>
> +  //   constexpr explicit(true) tuple(UTuple&&);
> +
> +  static_assert( !std::is_convertible_v<tuple_like_t<A>, tuple<B, B, B>> );
> +
> +  tuple_like_t<A> tuple_like;
> +
> +  [&] {
> +    tuple<B, B, B> t3b{tuple_like};
> +    VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && 
> std::get<2>(t3b).m == 0 );
> +  }();
> +  [&] {
> +    tuple<B, B, B> t3b{std::move(tuple_like)};
> +    VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && 
> std::get<2>(t3b).m == 1 );
> +  }();
> +  [&] {
> +    tuple<B, B, B> t3b{std::as_const(tuple_like)};
> +    VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && 
> std::get<2>(t3b).m == 2 );
> +  }();
> +  [&] {
> +    tuple<B, B, B> t3b{std::move(std::as_const(tuple_like))};
> +    VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && 
> std::get<2>(t3b).m == 3 );
> +  }();
> +
> +  return true;
> +}
> +
> +namespace alloc
> +{
> +  struct B02 {
> +    int m;
> +    explicit B02(A&);
> +    explicit B02(A&&);
> +    explicit B02(const A&);
> +    explicit B02(const A&&);
> +    explicit constexpr B02(allocator_arg_t, allocator<int>, A&) : m(0) { }
> +    explicit constexpr B02(allocator_arg_t, allocator<int>, A&&) : m(1) { }
> +    explicit constexpr B02(allocator_arg_t, allocator<int>, const A&) : m(2) 
> { }
> +    explicit constexpr B02(allocator_arg_t, allocator<int>, const A&&) : 
> m(3) { }
> +  };
> +
> +  template<template<typename> class tuple_like_t>
> +  constexpr bool
> +  test02()
> +  {
> +    using B = B02;
> +
> +    // template<tuple-like UTuple>
> +    //   constexpr explicit(true) tuple(allocator_arg_t, const Alloc&, 
> UTuple&&);
> +
> +    static_assert( !std::is_convertible_v<tuple_like_t<A>, tuple<B, B, B>> );
> +
> +    tuple_like_t<A> tuple_like;
> +
> +    [&] {
> +      tuple<B, B, B> t3b{allocator_arg, allocator<int>{}, tuple_like};
> +      VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && 
> std::get<2>(t3b).m == 0 );
> +    }();
> +    [&] {
> +      tuple<B, B, B> t3b{allocator_arg, allocator<int>{}, 
> std::move(tuple_like)};
> +      VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && 
> std::get<2>(t3b).m == 1 );
> +    }();
> +    [&] {
> +      tuple<B, B, B> t3b{allocator_arg, allocator<int>{}, 
> std::as_const(tuple_like)};
> +      VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && 
> std::get<2>(t3b).m == 2 );
> +    }();
> +    [&] {
> +      tuple<B, B, B> t3b{allocator_arg, allocator<int>{}, 
> std::move(std::as_const(tuple_like))};
> +      VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && 
> std::get<2>(t3b).m == 3 );
> +    }();
> +
> +    return true;
> +  }
> +}
> +
> +
> +template<template<typename> class tuple_like_t>
> +constexpr bool
> +test03()
> +{
> +  struct B {
> +    int m;
> +    constexpr B& operator=(A&) { m = 0; return *this; }
> +    constexpr B& operator=(A&&) { m = 1; return *this; }
> +    constexpr B& operator=(const A&) { m = 2; return *this; }
> +    constexpr B& operator=(const A&&) { m = 3; return *this; }
> +  };
> +
> +  // template<tuple-like UTuple>
> +  //   constexpr tuple& operator=(UTuple&&);
> +
> +  tuple_like_t<A> tuple_like;
> +
> +  tuple<B, B, B> t3b;
> +  t3b = tuple_like;
> +  VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && 
> std::get<2>(t3b).m == 0 );
> +  t3b = std::move(tuple_like);
> +  VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && 
> std::get<2>(t3b).m == 1 );
> +  t3b = std::as_const(tuple_like);
> +  VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && 
> std::get<2>(t3b).m == 2 );
> +  t3b = std::move(std::as_const(tuple_like));
> +  VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && 
> std::get<2>(t3b).m == 3 );
> +
> +  return true;
> +}
> +
> +template<template<typename> class tuple_like_t>
> +constexpr bool
> +test04()
> +{
> +  struct B {
> +    mutable int m;
> +    constexpr const B& operator=(A&) const { m = 0; return *this; }
> +    constexpr const B& operator=(A&&) const { m = 1; return *this; }
> +    constexpr const B& operator=(const A&) const { m = 2; return *this; }
> +    constexpr const B& operator=(const A&&) const { m = 3; return *this; }
> +  };
> +
> +  // template<tuple-like UTuple>
> +  //   constexpr const tuple& operator=(UTuple&&) const;
> +
> +  tuple_like_t<A> tuple_like;
> +
> +  const tuple<B, B, B> t3b;
> +  t3b = tuple_like;
> +  VERIFY( std::get<0>(t3b).m == 0 && std::get<1>(t3b).m == 0 && 
> std::get<2>(t3b).m == 0 );
> +  t3b = std::move(tuple_like);
> +  VERIFY( std::get<0>(t3b).m == 1 && std::get<1>(t3b).m == 1 && 
> std::get<2>(t3b).m == 1 );
> +  t3b = std::as_const(tuple_like);
> +  VERIFY( std::get<0>(t3b).m == 2 && std::get<1>(t3b).m == 2 && 
> std::get<2>(t3b).m == 2 );
> +  t3b = std::move(std::as_const(tuple_like));
> +  VERIFY( std::get<0>(t3b).m == 3 && std::get<1>(t3b).m == 3 && 
> std::get<2>(t3b).m == 3 );
> +
> +  return true;
> +}
> +
> +template<template<typename> class tuple_like_t>
> +constexpr bool
> +test05()
> +{
> +  // template<tuple-like UTuple>
> +  //   constexpr bool operator==(const tuple&, const UTuple&);
> +
> +  static_assert( tuple{1, 2, 3} == tuple_like_t{1, 2, 3} );
> +  static_assert( tuple{1, 2, 4} != tuple_like_t{1, 2, 3} );
> +  static_assert( tuple_like_t{1, 2, 3} == tuple{1, 2, 3} );
> +  static_assert( tuple_like_t{1, 2, 3} != tuple{1, 2, 4} );
> +
> +  // template<tuple-like UTuple>
> +  //   constexpr bool operator<=>const tuple&, const UTuple&);
> +
> +  static_assert( (tuple{1, 2, 3} <=> tuple_like_t{1, 2, 3}) == 
> std::strong_ordering::equal );
> +  static_assert( (tuple{1, 2, 4} <=> tuple_like_t{1, 2, 3}) == 
> std::strong_ordering::greater );
> +  static_assert( (tuple_like_t{1, 2, 3} <=> tuple{1, 2, 3}) == 
> std::strong_ordering::equal );
> +  static_assert( (tuple_like_t{1, 2, 3} <=> tuple{1, 2, 4}) == 
> std::strong_ordering::less  );
> +
> +  static_assert( tuple{1, 2, 4} > tuple_like_t{1, 2, 3} );
> +  static_assert( tuple_like_t{1, 2, 3} < tuple{1, 2, 4} );
> +
> +  // template<tuple-like TTuple, tuple-like UTuple, ...>
> +  //   struct basic_common_reference<TTuple, UTuple, ...>;
> +
> +  static_assert( std::same_as<std::common_reference_t<tuple_like_t<int>,
> +                                                   tuple<int, long, int>>,
> +                           tuple<int, long, int>> );
> +
> +  static_assert( std::same_as<std::common_reference_t<tuple<int, long, int>,
> +                                                   tuple_like_t<int>>,
> +                           tuple<int, long, int>> );
> +
> +  // template<tuple-like TTuple, tuple-like UTuple>
> +  //   struct common_type<TTuple, UTuple>;
> +
> +  static_assert( std::same_as<std::common_type_t<tuple_like_t<const int&>,
> +                                              tuple<int, long, int>>,
> +                           tuple<int, long, int>> );
> +
> +  static_assert( std::same_as<std::common_type_t<tuple<int, long, int>,
> +                                              tuple_like_t<const int&>>,
> +                           tuple<int, long, int>> );
> +
> +  return true;
> +}
> +
> +template<typename T>
> +using tuple_like_array = array<T, 3>;
> +
> +int
> +main()
> +{
> +  static_assert( test01<tuple_like_array>() );
> +  static_assert( alloc::test01<tuple_like_array>() );
> +  static_assert( test02<tuple_like_array>() );
> +  static_assert( alloc::test02<tuple_like_array>() );
> +  static_assert( test03<tuple_like_array>() );
> +  static_assert( test04<tuple_like_array>() );
> +  static_assert( test05<tuple_like_array>() );
> +}
> diff --git a/libstdc++-v3/testsuite/std/ranges/zip/1.cc 
> b/libstdc++-v3/testsuite/std/ranges/zip/1.cc
> index b7717aed92c..672a8c356d9 100644
> --- a/libstdc++-v3/testsuite/std/ranges/zip/1.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/zip/1.cc
> @@ -41,8 +41,8 @@ test01()
>    VERIFY( i2 == z2.end() );
>    VERIFY( ranges::size(z2) == 2 );
>    VERIFY( ranges::size(std::as_const(z2)) == 2 );
> -  VERIFY( z2[0].first == 1 && z2[0].second == 3 );
> -  VERIFY( z2[1].first == 2 && z2[1].second == 4 );
> +  VERIFY( std::get<0>(z2[0]) == 1 && std::get<1>(z2[0]) == 3 );
> +  VERIFY( std::get<0>(z2[1]) == 2 && std::get<1>(z2[1]) == 4 );
>    for (const auto [x, y] : z2)
>      {
>        VERIFY( y - x == 2 );
> -- 
> 2.43.0.386.ge02ecfcc53
> 
> 

Reply via email to