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.

> +
>    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.



> 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?


> +       }
> +#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.

Reply via email to