libstdc++: Optimize determination of std::tuple_cat return type
Commit Message
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
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
>
>
@@ -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).
@@ -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.