libstdc++: Optimize determination of std::tuple_cat return type

Message ID 20250916151843.2680202-1-jwakely@redhat.com
State Committed
Commit 5d774ec80bcb633a393790154c9969218b7c782f
Headers
Series libstdc++: Optimize determination of std::tuple_cat return type |

Commit Message

Jonathan Wakely Sept. 16, 2025, 3:17 p.m. UTC
  The std::tuple_cat function has to determine a std::tuple return type
from zero or more tuple-like arguments. This uses the __make_tuple class
template to transform a tuple-like type into a std::tuple, and the
__combine_tuples class template to combine zero or more std::tuple types
into a single std::tuple type.

This change optimizes the __make_tuple class template to use an
_Index_tuple and pack expansion instead of recursive instantiation, and
optimizes __combine_tuples to use fewer levels of recursion.

For ranges::adjacent_view's __detail::__repeated_tuple helper we can
just use the __make_tuple class template directly, instead of doing
overload resolution on std::tuple_cat to get its return type.

libstdc++-v3/ChangeLog:

	* include/std/ranges (__detail::__repeated_tuple): Use
	__make_tuple helper alias directly, instead of doing overload
	resolution on std::tuple_cat.
	* include/std/tuple (__make_tuple_impl): Remove.
	(__do_make_tuple): Replace recursion with _Index_tuple and pack
	expansion.
	(__make_tuple): Adjust to new __do_make_tuple definition.
	(__combine_tuples<tuple<T1s...>, tuple<T2s...>, Rem...>): Replace
	with a partial specialization for exactly two tuples and a
	partial specialization for three or more tuples.
---

Tested powerpc64le-linux.

 libstdc++-v3/include/std/ranges |  2 +-
 libstdc++-v3/include/std/tuple  | 51 ++++++++++++++++-----------------
 2 files changed, 26 insertions(+), 27 deletions(-)
  

Comments

Patrick Palka Sept. 16, 2025, 4:48 p.m. UTC | #1
On Tue, 16 Sep 2025, Jonathan Wakely wrote:

> The std::tuple_cat function has to determine a std::tuple return type
> from zero or more tuple-like arguments. This uses the __make_tuple class
> template to transform a tuple-like type into a std::tuple, and the
> __combine_tuples class template to combine zero or more std::tuple types
> into a single std::tuple type.
> 
> This change optimizes the __make_tuple class template to use an
> _Index_tuple and pack expansion instead of recursive instantiation, and
> optimizes __combine_tuples to use fewer levels of recursion.
> 
> For ranges::adjacent_view's __detail::__repeated_tuple helper we can
> just use the __make_tuple class template directly, instead of doing
> overload resolution on std::tuple_cat to get its return type.
> 
> libstdc++-v3/ChangeLog:
> 
> 	* include/std/ranges (__detail::__repeated_tuple): Use
> 	__make_tuple helper alias directly, instead of doing overload
> 	resolution on std::tuple_cat.
> 	* include/std/tuple (__make_tuple_impl): Remove.
> 	(__do_make_tuple): Replace recursion with _Index_tuple and pack
> 	expansion.
> 	(__make_tuple): Adjust to new __do_make_tuple definition.
> 	(__combine_tuples<tuple<T1s...>, tuple<T2s...>, Rem...>): Replace
> 	with a partial specialization for exactly two tuples and a
> 	partial specialization for three or more tuples.

Nice! LGTM

> ---
> 
> Tested powerpc64le-linux.
> 
>  libstdc++-v3/include/std/ranges |  2 +-
>  libstdc++-v3/include/std/tuple  | 51 ++++++++++++++++-----------------
>  2 files changed, 26 insertions(+), 27 deletions(-)
> 
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index f493da56203b..6c64f4c36213 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -5383,7 +5383,7 @@ namespace views::__adaptor
>    {
>      // Yields tuple<_Tp, ..., _Tp> with _Nm elements.
>      template<typename _Tp, size_t _Nm>
> -      using __repeated_tuple = decltype(std::tuple_cat(std::declval<array<_Tp, _Nm>>()));
> +      using __repeated_tuple = typename __make_tuple<array<_Tp, _Nm>>::__type;
>  
>      // For a functor F that is callable with N arguments, the expression
>      // declval<__unarize<F, N>>(x) is equivalent to declval<F>(x, ..., x).
> diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
> index 2e6499eab22d..0ca616f1b4fb 100644
> --- a/libstdc++-v3/include/std/tuple
> +++ b/libstdc++-v3/include/std/tuple
> @@ -2681,54 +2681,53 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      { return tuple<_Elements&&...>(std::forward<_Elements>(__args)...); }
>  
>    /// @cond undocumented
> -  template<size_t, typename, typename, size_t>
> -    struct __make_tuple_impl;
> +  template<typename _Tuple, typename _Idx_tuple>
> +    struct __do_make_tuple;
>  
> -  template<size_t _Idx, typename _Tuple, typename... _Tp, size_t _Nm>
> -    struct __make_tuple_impl<_Idx, tuple<_Tp...>, _Tuple, _Nm>
> -    : __make_tuple_impl<_Idx + 1,
> -			tuple<_Tp..., __tuple_element_t<_Idx, _Tuple>>,
> -			_Tuple, _Nm>
> -    { };
> -
> -  template<size_t _Nm, typename _Tuple, typename... _Tp>
> -    struct __make_tuple_impl<_Nm, tuple<_Tp...>, _Tuple, _Nm>
> +  template<typename _Tuple, size_t... _Idx>
> +    struct __do_make_tuple<_Tuple, _Index_tuple<_Idx...>>
>      {
> -      typedef tuple<_Tp...> __type;
> +      using __type = tuple<__tuple_element_t<_Idx, _Tuple>...>;
>      };
>  
> -  template<typename _Tuple>
> -    struct __do_make_tuple
> -    : __make_tuple_impl<0, tuple<>, _Tuple, tuple_size<_Tuple>::value>
> -    { };
> -
>    // Returns the std::tuple equivalent of a tuple-like type.
> -  template<typename _Tuple>
> +  template<typename _Tuple,
> +	   typename _Tup = __remove_cvref_t<_Tuple>,
> +	   typename _Indices = _Build_index_tuple<tuple_size<_Tup>::value>>
>      struct __make_tuple
> -    : public __do_make_tuple<__remove_cvref_t<_Tuple>>
> +    : __do_make_tuple<_Tup, typename _Indices::__type>
>      { };
>  
> -  // Combines several std::tuple's into a single one.
> +  // Combines several std::tuple types into a single one.
>    template<typename...>
>      struct __combine_tuples;
>  
>    template<>
>      struct __combine_tuples<>
>      {
> -      typedef tuple<> __type;
> +      using __type = tuple<>;
>      };
>  
>    template<typename... _Ts>
>      struct __combine_tuples<tuple<_Ts...>>
>      {
> -      typedef tuple<_Ts...> __type;
> +      using __type = tuple<_Ts...>;
>      };
>  
> -  template<typename... _T1s, typename... _T2s, typename... _Rem>
> -    struct __combine_tuples<tuple<_T1s...>, tuple<_T2s...>, _Rem...>
> +  template<typename... _T1s, typename... _T2s>
> +    struct __combine_tuples<tuple<_T1s...>, tuple<_T2s...>>
>      {
> -      typedef typename __combine_tuples<tuple<_T1s..., _T2s...>,
> -					_Rem...>::__type __type;
> +      using __type = tuple<_T1s..., _T2s...>;
> +    };
> +
> +  template<typename... _T1s, typename... _T2s, typename... _T3s,
> +	   typename... _Rem>
> +    struct __combine_tuples<tuple<_T1s...>, tuple<_T2s...>, tuple<_T3s...>,
> +			    _Rem...>
> +    {
> +      using _First = tuple<_T1s..., _T2s..., _T3s...>;
> +      using _Second = typename __combine_tuples<_Rem...>::__type;
> +      using __type = typename __combine_tuples<_First, _Second>::__type;
>      };
>  
>    // Computes the result type of tuple_cat given a set of tuple-like types.
> -- 
> 2.51.0
> 
>
  

Patch

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index f493da56203b..6c64f4c36213 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -5383,7 +5383,7 @@  namespace views::__adaptor
   {
     // Yields tuple<_Tp, ..., _Tp> with _Nm elements.
     template<typename _Tp, size_t _Nm>
-      using __repeated_tuple = decltype(std::tuple_cat(std::declval<array<_Tp, _Nm>>()));
+      using __repeated_tuple = typename __make_tuple<array<_Tp, _Nm>>::__type;
 
     // For a functor F that is callable with N arguments, the expression
     // declval<__unarize<F, N>>(x) is equivalent to declval<F>(x, ..., x).
diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index 2e6499eab22d..0ca616f1b4fb 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -2681,54 +2681,53 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return tuple<_Elements&&...>(std::forward<_Elements>(__args)...); }
 
   /// @cond undocumented
-  template<size_t, typename, typename, size_t>
-    struct __make_tuple_impl;
+  template<typename _Tuple, typename _Idx_tuple>
+    struct __do_make_tuple;
 
-  template<size_t _Idx, typename _Tuple, typename... _Tp, size_t _Nm>
-    struct __make_tuple_impl<_Idx, tuple<_Tp...>, _Tuple, _Nm>
-    : __make_tuple_impl<_Idx + 1,
-			tuple<_Tp..., __tuple_element_t<_Idx, _Tuple>>,
-			_Tuple, _Nm>
-    { };
-
-  template<size_t _Nm, typename _Tuple, typename... _Tp>
-    struct __make_tuple_impl<_Nm, tuple<_Tp...>, _Tuple, _Nm>
+  template<typename _Tuple, size_t... _Idx>
+    struct __do_make_tuple<_Tuple, _Index_tuple<_Idx...>>
     {
-      typedef tuple<_Tp...> __type;
+      using __type = tuple<__tuple_element_t<_Idx, _Tuple>...>;
     };
 
-  template<typename _Tuple>
-    struct __do_make_tuple
-    : __make_tuple_impl<0, tuple<>, _Tuple, tuple_size<_Tuple>::value>
-    { };
-
   // Returns the std::tuple equivalent of a tuple-like type.
-  template<typename _Tuple>
+  template<typename _Tuple,
+	   typename _Tup = __remove_cvref_t<_Tuple>,
+	   typename _Indices = _Build_index_tuple<tuple_size<_Tup>::value>>
     struct __make_tuple
-    : public __do_make_tuple<__remove_cvref_t<_Tuple>>
+    : __do_make_tuple<_Tup, typename _Indices::__type>
     { };
 
-  // Combines several std::tuple's into a single one.
+  // Combines several std::tuple types into a single one.
   template<typename...>
     struct __combine_tuples;
 
   template<>
     struct __combine_tuples<>
     {
-      typedef tuple<> __type;
+      using __type = tuple<>;
     };
 
   template<typename... _Ts>
     struct __combine_tuples<tuple<_Ts...>>
     {
-      typedef tuple<_Ts...> __type;
+      using __type = tuple<_Ts...>;
     };
 
-  template<typename... _T1s, typename... _T2s, typename... _Rem>
-    struct __combine_tuples<tuple<_T1s...>, tuple<_T2s...>, _Rem...>
+  template<typename... _T1s, typename... _T2s>
+    struct __combine_tuples<tuple<_T1s...>, tuple<_T2s...>>
     {
-      typedef typename __combine_tuples<tuple<_T1s..., _T2s...>,
-					_Rem...>::__type __type;
+      using __type = tuple<_T1s..., _T2s...>;
+    };
+
+  template<typename... _T1s, typename... _T2s, typename... _T3s,
+	   typename... _Rem>
+    struct __combine_tuples<tuple<_T1s...>, tuple<_T2s...>, tuple<_T3s...>,
+			    _Rem...>
+    {
+      using _First = tuple<_T1s..., _T2s..., _T3s...>;
+      using _Second = typename __combine_tuples<_Rem...>::__type;
+      using __type = typename __combine_tuples<_First, _Second>::__type;
     };
 
   // Computes the result type of tuple_cat given a set of tuple-like types.