diff mbox series

[committed] libstdc++: Optimize std::tuple_element and std::tuple_size_v

Message ID 20211104183322.2123606-1-jwakely@redhat.com
State New
Headers show
Series [committed] libstdc++: Optimize std::tuple_element and std::tuple_size_v | expand

Commit Message

Jonathan Wakely Nov. 4, 2021, 6:33 p.m. UTC
Tested powerpc64le-linux, committed to trunk.


This reduces the number of class template instantiations needed for code
using tuples, by reusing _Nth_type in tuple_element and specializing
tuple_size_v for tuple, pair and array (and const-qualified versions of
them).

Also define the _Nth_type primary template as a complete type (but with
no nested 'type' member).  This avoids "invalid use of incomplete type"
errors for out-of-range specializations of tuple_element. Those errors
would probably be confusing and unhelpful for users. We already have
a user-friendly static assert in tuple_element itself.

Also ensure that tuple_size_v is available whenever tuple_size is (as
proposed by LWG 3387). We already do that for tuple_element_t.

libstdc++-v3/ChangeLog:

	* include/bits/stl_pair.h (tuple_size_v): Define partial
	specializations for std::pair.
	* include/bits/utility.h (_Nth_type): Move definition here
	and define primary template.
	(tuple_size_v): Move definition here.
	* include/std/array (tuple_size_v): Define partial
	specializations for std::array.
	* include/std/tuple (tuple_size_v): Move primary template to
	<bits/utility.h>.  Define partial specializations for
	std::tuple.
	(tuple_element): Change definition to use _Nth_type.
	* include/std/variant (_Nth_type): Move to <bits/utility.h>.
	(variant_alternative, variant): Adjust qualification of
	_Nth_type.
	* testsuite/20_util/tuple/element_access/get_neg.cc: Prune
	additional errors from _Nth_type.
---
 libstdc++-v3/include/bits/stl_pair.h          |  8 +++
 libstdc++-v3/include/bits/utility.h           | 51 +++++++++++++++-
 libstdc++-v3/include/std/array                |  8 +++
 libstdc++-v3/include/std/tuple                | 39 +++++--------
 libstdc++-v3/include/std/variant              | 58 ++-----------------
 .../20_util/tuple/element_access/get_neg.cc   |  1 +
 6 files changed, 84 insertions(+), 81 deletions(-)

Comments

Patrick Palka Nov. 5, 2021, 9:19 p.m. UTC | #1
On Thu, Nov 4, 2021 at 2:34 PM Jonathan Wakely via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> Tested powerpc64le-linux, committed to trunk.
>
>
> This reduces the number of class template instantiations needed for code
> using tuples, by reusing _Nth_type in tuple_element and specializing
> tuple_size_v for tuple, pair and array (and const-qualified versions of
> them).
>
> Also define the _Nth_type primary template as a complete type (but with
> no nested 'type' member).  This avoids "invalid use of incomplete type"
> errors for out-of-range specializations of tuple_element. Those errors
> would probably be confusing and unhelpful for users. We already have
> a user-friendly static assert in tuple_element itself.
>
> Also ensure that tuple_size_v is available whenever tuple_size is (as
> proposed by LWG 3387). We already do that for tuple_element_t.
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/stl_pair.h (tuple_size_v): Define partial
>         specializations for std::pair.
>         * include/bits/utility.h (_Nth_type): Move definition here
>         and define primary template.
>         (tuple_size_v): Move definition here.
>         * include/std/array (tuple_size_v): Define partial
>         specializations for std::array.
>         * include/std/tuple (tuple_size_v): Move primary template to
>         <bits/utility.h>.  Define partial specializations for
>         std::tuple.
>         (tuple_element): Change definition to use _Nth_type.
>         * include/std/variant (_Nth_type): Move to <bits/utility.h>.
>         (variant_alternative, variant): Adjust qualification of
>         _Nth_type.
>         * testsuite/20_util/tuple/element_access/get_neg.cc: Prune
>         additional errors from _Nth_type.
> ---
>  libstdc++-v3/include/bits/stl_pair.h          |  8 +++
>  libstdc++-v3/include/bits/utility.h           | 51 +++++++++++++++-
>  libstdc++-v3/include/std/array                |  8 +++
>  libstdc++-v3/include/std/tuple                | 39 +++++--------
>  libstdc++-v3/include/std/variant              | 58 ++-----------------
>  .../20_util/tuple/element_access/get_neg.cc   |  1 +
>  6 files changed, 84 insertions(+), 81 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
> index 5b400daf97f..6081e0c7fe9 100644
> --- a/libstdc++-v3/include/bits/stl_pair.h
> +++ b/libstdc++-v3/include/bits/stl_pair.h
> @@ -771,6 +771,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      struct tuple_element<1, pair<_Tp1, _Tp2>>
>      { typedef _Tp2 type; };
>
> +#if __cplusplus >= 201703L
> +  template<typename _Tp1, typename _Tp2>
> +    inline constexpr size_t tuple_size_v<pair<_Tp1, _Tp2>> = 2;
> +
> +  template<typename _Tp1, typename _Tp2>
> +    inline constexpr size_t tuple_size_v<const pair<_Tp1, _Tp2>> = 2;
> +#endif
> +
>    /// @cond undocumented
>    template<size_t _Int>
>      struct __pair_get;
> diff --git a/libstdc++-v3/include/bits/utility.h b/libstdc++-v3/include/bits/utility.h
> index fce52a4530d..c9ffa008217 100644
> --- a/libstdc++-v3/include/bits/utility.h
> +++ b/libstdc++-v3/include/bits/utility.h
> @@ -70,6 +70,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      struct tuple_size<const volatile __enable_if_has_tuple_size<_Tp>>
>      : public tuple_size<_Tp> { };
>
> +#if __cplusplus >= 201703L
> +  template<typename _Tp>
> +    inline constexpr size_t tuple_size_v = tuple_size<_Tp>::value;
> +#endif
> +
>    /// Gives the type of the ith element of a given tuple type.
>    template<size_t __i, typename _Tp>
>      struct tuple_element;
> @@ -97,8 +102,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      };
>
>  #if __cplusplus >= 201402L
> -// The standard says this macro and alias template should be in <tuple>
> -// but we define them here, to be available in <utility> and <array> too.
> +// The standard says this macro and alias template should be in <tuple> but we
> +// we define them here, to be available in <array>, <utility> and <ranges> too.
> +// _GLIBCXX_RESOLVE_LIB_DEFECTS
> +// 3378. tuple_size_v/tuple_element_t should be available when
> +//       tuple_size/tuple_element are
>  #define __cpp_lib_tuple_element_t 201402L
>
>    template<size_t __i, typename _Tp>
> @@ -195,6 +203,45 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  #endif // C++17
>  #endif // C++14
>
> +  template<size_t _Np, typename... _Types>
> +    struct _Nth_type
> +    { };
> +
> +  template<typename _Tp0, typename... _Rest>
> +    struct _Nth_type<0, _Tp0, _Rest...>
> +    { using type = _Tp0; };
> +
> +  template<typename _Tp0, typename _Tp1, typename... _Rest>
> +    struct _Nth_type<1, _Tp0, _Tp1, _Rest...>
> +    { using type = _Tp1; };
> +
> +  template<typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
> +    struct _Nth_type<2, _Tp0, _Tp1, _Tp2, _Rest...>
> +    { using type = _Tp2; };
> +
> +  template<size_t _Np, typename _Tp0, typename _Tp1, typename _Tp2,
> +          typename... _Rest>
> +#if __cpp_concepts
> +    requires (_Np >= 3)
> +#endif
> +    struct _Nth_type<_Np, _Tp0, _Tp1, _Tp2, _Rest...>
> +    : _Nth_type<_Np - 3, _Rest...>
> +    { };
> +
> +#if ! __cpp_concepts // Need additional specializations to avoid ambiguities.
> +  template<typename _Tp0, typename _Tp1, typename... _Rest>
> +    struct _Nth_type<0, _Tp0, _Tp1, _Rest...>
> +    { using type = _Tp0; };
> +
> +  template<typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
> +    struct _Nth_type<0, _Tp0, _Tp1, _Tp2, _Rest...>
> +    { using type = _Tp0; };
> +
> +  template<typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
> +    struct _Nth_type<1, _Tp0, _Tp1, _Tp2, _Rest...>
> +    { using type = _Tp1; };
> +#endif
> +
>  _GLIBCXX_END_NAMESPACE_VERSION
>  } // namespace
>
> diff --git a/libstdc++-v3/include/std/array b/libstdc++-v3/include/std/array
> index 3e12d35157c..413f8e2be01 100644
> --- a/libstdc++-v3/include/std/array
> +++ b/libstdc++-v3/include/std/array
> @@ -481,6 +481,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        using type = _Tp;
>      };
>
> +#if __cplusplus >= 201703L
> +  template<typename _Tp, size_t _Nm>
> +    inline constexpr size_t tuple_size_v<array<_Tp, _Nm>> = _Nm;
> +
> +  template<typename _Tp, size_t _Nm>
> +    inline constexpr size_t tuple_size_v<const array<_Tp, _Nm>> = _Nm;
> +#endif
> +
>    template<typename _Tp, size_t _Nm>
>      struct __is_tuple_like_impl<array<_Tp, _Nm>> : true_type
>      { };
> diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
> index aaee0b8826a..b82cdf12569 100644
> --- a/libstdc++-v3/include/std/tuple
> +++ b/libstdc++-v3/include/std/tuple
> @@ -1344,36 +1344,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      struct tuple_size<tuple<_Elements...>>
>      : public integral_constant<size_t, sizeof...(_Elements)> { };
>
> -#if __cplusplus > 201402L
> -  template <typename _Tp>
> -    inline constexpr size_t tuple_size_v = tuple_size<_Tp>::value;
> +#if __cplusplus >= 201703L
> +  template<typename... _Types>
> +    inline constexpr size_t tuple_size_v<tuple<_Types>>

Missing ... after _Types?

> +      = sizeof...(_Types);
> +
> +  template<typename... _Types>
> +    inline constexpr size_t tuple_size_v<const tuple<_Types>>

Same here.  (I guess now's a good time to fix PR100652!)

> +      = sizeof...(_Types);
>  #endif
>
> -  /**
> -   * Recursive case for tuple_element: strip off the first element in
> -   * the tuple and retrieve the (i-1)th element of the remaining tuple.
> -   */
> -  template<size_t __i, typename _Head, typename... _Tail>
> -    struct tuple_element<__i, tuple<_Head, _Tail...> >
> -    : tuple_element<__i - 1, tuple<_Tail...> > { };
> -
> -  /**
> -   * Basis case for tuple_element: The first element is the one we're seeking.
> -   */
> -  template<typename _Head, typename... _Tail>
> -    struct tuple_element<0, tuple<_Head, _Tail...> >
> +  /// Trait to get the Ith element type from a tuple.
> +  template<size_t __i, typename... _Types>
> +    struct tuple_element<__i, tuple<_Types...>>
>      {
> -      typedef _Head type;
> -    };
> +      static_assert(__i < sizeof...(_Types), "tuple index must be in range");
>
> -  /**
> -   * Error case for tuple_element: invalid index.
> -   */
> -  template<size_t __i>
> -    struct tuple_element<__i, tuple<>>
> -    {
> -      static_assert(__i < tuple_size<tuple<>>::value,
> -         "tuple index must be in range");
> +      using type = typename _Nth_type<__i, _Types...>::type;
>      };
>
>    template<size_t __i, typename _Head, typename... _Tail>
> diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
> index c4c307b7bb2..993ce3dba91 100644
> --- a/libstdc++-v3/include/std/variant
> +++ b/libstdc++-v3/include/std/variant
> @@ -54,51 +54,6 @@ namespace std _GLIBCXX_VISIBILITY(default)
>  {
>  _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> -namespace __detail
> -{
> -namespace __variant
> -{
> -  template<size_t _Np, typename... _Types>
> -    struct _Nth_type;
> -
> -  template<typename _Tp0, typename... _Rest>
> -    struct _Nth_type<0, _Tp0, _Rest...>
> -    { using type = _Tp0; };
> -
> -  template<typename _Tp0, typename _Tp1, typename... _Rest>
> -    struct _Nth_type<1, _Tp0, _Tp1, _Rest...>
> -    { using type = _Tp1; };
> -
> -  template<typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
> -    struct _Nth_type<2, _Tp0, _Tp1, _Tp2, _Rest...>
> -    { using type = _Tp2; };
> -
> -  template<size_t _Np, typename _Tp0, typename _Tp1, typename _Tp2,
> -          typename... _Rest>
> -#if __cpp_concepts
> -    requires (_Np >= 3)
> -#endif
> -    struct _Nth_type<_Np, _Tp0, _Tp1, _Tp2, _Rest...>
> -    : _Nth_type<_Np - 3, _Rest...>
> -    { };
> -
> -#if ! __cpp_concepts // Need additional specializations to avoid ambiguities.
> -  template<typename _Tp0, typename _Tp1, typename... _Rest>
> -    struct _Nth_type<0, _Tp0, _Tp1, _Rest...>
> -    { using type = _Tp0; };
> -
> -  template<typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
> -    struct _Nth_type<0, _Tp0, _Tp1, _Tp2, _Rest...>
> -    { using type = _Tp0; };
> -
> -  template<typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
> -    struct _Nth_type<1, _Tp0, _Tp1, _Tp2, _Rest...>
> -    { using type = _Tp1; };
> -#endif
> -
> -} // namespace __variant
> -} // namespace __detail
> -
>  #if __cplusplus >= 202002L && __cpp_concepts
>  // P2231R1 constexpr needs constexpr unions and constrained destructors.
>  # define __cpp_lib_variant 202106L
> @@ -145,8 +100,7 @@ namespace __variant
>      {
>        static_assert(_Np < sizeof...(_Types));
>
> -      using type
> -       = typename __detail::__variant::_Nth_type<_Np, _Types...>::type;
> +      using type = typename _Nth_type<_Np, _Types...>::type;
>      };
>
>    template<size_t _Np, typename _Variant>
> @@ -1442,8 +1396,7 @@ namespace __variant
>           = __detail::__variant::__accepted_index<_Tp, variant>::value;
>
>        template<size_t _Np, typename = enable_if_t<(_Np < sizeof...(_Types))>>
> -       using __to_type
> -         = typename __detail::__variant::_Nth_type<_Np, _Types...>::type;
> +       using __to_type = typename _Nth_type<_Np, _Types...>::type;
>
>        template<typename _Tp, typename = enable_if_t<__not_self<_Tp>>>
>         using __accepted_type = __to_type<__accepted_index<_Tp>>;
> @@ -1580,7 +1533,7 @@ namespace __variant
>         emplace(_Args&&... __args)
>         {
>           namespace __variant = std::__detail::__variant;
> -         using type = typename __variant::_Nth_type<_Np, _Types...>::type;
> +         using type = typename _Nth_type<_Np, _Types...>::type;
>           // Provide the strong exception-safety guarantee when possible,
>           // to avoid becoming valueless.
>           if constexpr (is_nothrow_constructible_v<type, _Args...>)
> @@ -1620,7 +1573,7 @@ namespace __variant
>         emplace(initializer_list<_Up> __il, _Args&&... __args)
>         {
>           namespace __variant = std::__detail::__variant;
> -         using type = typename __variant::_Nth_type<_Np, _Types...>::type;
> +         using type = typename _Nth_type<_Np, _Types...>::type;
>           // Provide the strong exception-safety guarantee when possible,
>           // to avoid becoming valueless.
>           if constexpr (is_nothrow_constructible_v<type,
> @@ -1803,8 +1756,7 @@ namespace __variant
>           constexpr size_t __max = 11; // "These go to eleven."
>
>           // The type of the first variant in the pack.
> -         using _V0
> -           = typename __detail::__variant::_Nth_type<0, _Variants...>::type;
> +         using _V0 = typename _Nth_type<0, _Variants...>::type;
>           // The number of alternatives in that first variant.
>           constexpr auto __n = variant_size_v<remove_reference_t<_V0>>;
>
> diff --git a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
> index 225bb6245a6..113a7fd62de 100644
> --- a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
> +++ b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
> @@ -60,3 +60,4 @@ test03()
>  }
>
>  // { dg-error "tuple index must be in range" "" { target *-*-* } 0 }
> +// { dg-prune-output "no type named 'type' in .*_Nth_type" }
> --
> 2.31.1
>
Jonathan Wakely Nov. 5, 2021, 9:45 p.m. UTC | #2
On Fri, 5 Nov 2021 at 21:19, Patrick Palka <ppalka@redhat.com> wrote:

> On Thu, Nov 4, 2021 at 2:34 PM Jonathan Wakely via Libstdc++
> <libstdc++@gcc.gnu.org> wrote:
> >
> > Tested powerpc64le-linux, committed to trunk.
> >
> >
> > This reduces the number of class template instantiations needed for code
> > using tuples, by reusing _Nth_type in tuple_element and specializing
> > tuple_size_v for tuple, pair and array (and const-qualified versions of
> > them).
> >
> > Also define the _Nth_type primary template as a complete type (but with
> > no nested 'type' member).  This avoids "invalid use of incomplete type"
> > errors for out-of-range specializations of tuple_element. Those errors
> > would probably be confusing and unhelpful for users. We already have
> > a user-friendly static assert in tuple_element itself.
> >
> > Also ensure that tuple_size_v is available whenever tuple_size is (as
> > proposed by LWG 3387). We already do that for tuple_element_t.
> >
> > libstdc++-v3/ChangeLog:
> >
> >         * include/bits/stl_pair.h (tuple_size_v): Define partial
> >         specializations for std::pair.
> >         * include/bits/utility.h (_Nth_type): Move definition here
> >         and define primary template.
> >         (tuple_size_v): Move definition here.
> >         * include/std/array (tuple_size_v): Define partial
> >         specializations for std::array.
> >         * include/std/tuple (tuple_size_v): Move primary template to
> >         <bits/utility.h>.  Define partial specializations for
> >         std::tuple.
> >         (tuple_element): Change definition to use _Nth_type.
> >         * include/std/variant (_Nth_type): Move to <bits/utility.h>.
> >         (variant_alternative, variant): Adjust qualification of
> >         _Nth_type.
> >         * testsuite/20_util/tuple/element_access/get_neg.cc: Prune
> >         additional errors from _Nth_type.
> > ---
> >  libstdc++-v3/include/bits/stl_pair.h          |  8 +++
> >  libstdc++-v3/include/bits/utility.h           | 51 +++++++++++++++-
> >  libstdc++-v3/include/std/array                |  8 +++
> >  libstdc++-v3/include/std/tuple                | 39 +++++--------
> >  libstdc++-v3/include/std/variant              | 58 ++-----------------
> >  .../20_util/tuple/element_access/get_neg.cc   |  1 +
> >  6 files changed, 84 insertions(+), 81 deletions(-)
> >
> > diff --git a/libstdc++-v3/include/bits/stl_pair.h
> b/libstdc++-v3/include/bits/stl_pair.h
> > index 5b400daf97f..6081e0c7fe9 100644
> > --- a/libstdc++-v3/include/bits/stl_pair.h
> > +++ b/libstdc++-v3/include/bits/stl_pair.h
> > @@ -771,6 +771,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >      struct tuple_element<1, pair<_Tp1, _Tp2>>
> >      { typedef _Tp2 type; };
> >
> > +#if __cplusplus >= 201703L
> > +  template<typename _Tp1, typename _Tp2>
> > +    inline constexpr size_t tuple_size_v<pair<_Tp1, _Tp2>> = 2;
> > +
> > +  template<typename _Tp1, typename _Tp2>
> > +    inline constexpr size_t tuple_size_v<const pair<_Tp1, _Tp2>> = 2;
> > +#endif
> > +
> >    /// @cond undocumented
> >    template<size_t _Int>
> >      struct __pair_get;
> > diff --git a/libstdc++-v3/include/bits/utility.h
> b/libstdc++-v3/include/bits/utility.h
> > index fce52a4530d..c9ffa008217 100644
> > --- a/libstdc++-v3/include/bits/utility.h
> > +++ b/libstdc++-v3/include/bits/utility.h
> > @@ -70,6 +70,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >      struct tuple_size<const volatile __enable_if_has_tuple_size<_Tp>>
> >      : public tuple_size<_Tp> { };
> >
> > +#if __cplusplus >= 201703L
> > +  template<typename _Tp>
> > +    inline constexpr size_t tuple_size_v = tuple_size<_Tp>::value;
> > +#endif
> > +
> >    /// Gives the type of the ith element of a given tuple type.
> >    template<size_t __i, typename _Tp>
> >      struct tuple_element;
> > @@ -97,8 +102,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >      };
> >
> >  #if __cplusplus >= 201402L
> > -// The standard says this macro and alias template should be in <tuple>
> > -// but we define them here, to be available in <utility> and <array>
> too.
> > +// The standard says this macro and alias template should be in <tuple>
> but we
> > +// we define them here, to be available in <array>, <utility> and
> <ranges> too.
> > +// _GLIBCXX_RESOLVE_LIB_DEFECTS
> > +// 3378. tuple_size_v/tuple_element_t should be available when
> > +//       tuple_size/tuple_element are
> >  #define __cpp_lib_tuple_element_t 201402L
> >
> >    template<size_t __i, typename _Tp>
> > @@ -195,6 +203,45 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >  #endif // C++17
> >  #endif // C++14
> >
> > +  template<size_t _Np, typename... _Types>
> > +    struct _Nth_type
> > +    { };
> > +
> > +  template<typename _Tp0, typename... _Rest>
> > +    struct _Nth_type<0, _Tp0, _Rest...>
> > +    { using type = _Tp0; };
> > +
> > +  template<typename _Tp0, typename _Tp1, typename... _Rest>
> > +    struct _Nth_type<1, _Tp0, _Tp1, _Rest...>
> > +    { using type = _Tp1; };
> > +
> > +  template<typename _Tp0, typename _Tp1, typename _Tp2, typename...
> _Rest>
> > +    struct _Nth_type<2, _Tp0, _Tp1, _Tp2, _Rest...>
> > +    { using type = _Tp2; };
> > +
> > +  template<size_t _Np, typename _Tp0, typename _Tp1, typename _Tp2,
> > +          typename... _Rest>
> > +#if __cpp_concepts
> > +    requires (_Np >= 3)
> > +#endif
> > +    struct _Nth_type<_Np, _Tp0, _Tp1, _Tp2, _Rest...>
> > +    : _Nth_type<_Np - 3, _Rest...>
> > +    { };
> > +
> > +#if ! __cpp_concepts // Need additional specializations to avoid
> ambiguities.
> > +  template<typename _Tp0, typename _Tp1, typename... _Rest>
> > +    struct _Nth_type<0, _Tp0, _Tp1, _Rest...>
> > +    { using type = _Tp0; };
> > +
> > +  template<typename _Tp0, typename _Tp1, typename _Tp2, typename...
> _Rest>
> > +    struct _Nth_type<0, _Tp0, _Tp1, _Tp2, _Rest...>
> > +    { using type = _Tp0; };
> > +
> > +  template<typename _Tp0, typename _Tp1, typename _Tp2, typename...
> _Rest>
> > +    struct _Nth_type<1, _Tp0, _Tp1, _Tp2, _Rest...>
> > +    { using type = _Tp1; };
> > +#endif
> > +
> >  _GLIBCXX_END_NAMESPACE_VERSION
> >  } // namespace
> >
> > diff --git a/libstdc++-v3/include/std/array
> b/libstdc++-v3/include/std/array
> > index 3e12d35157c..413f8e2be01 100644
> > --- a/libstdc++-v3/include/std/array
> > +++ b/libstdc++-v3/include/std/array
> > @@ -481,6 +481,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >        using type = _Tp;
> >      };
> >
> > +#if __cplusplus >= 201703L
> > +  template<typename _Tp, size_t _Nm>
> > +    inline constexpr size_t tuple_size_v<array<_Tp, _Nm>> = _Nm;
> > +
> > +  template<typename _Tp, size_t _Nm>
> > +    inline constexpr size_t tuple_size_v<const array<_Tp, _Nm>> = _Nm;
> > +#endif
> > +
> >    template<typename _Tp, size_t _Nm>
> >      struct __is_tuple_like_impl<array<_Tp, _Nm>> : true_type
> >      { };
> > diff --git a/libstdc++-v3/include/std/tuple
> b/libstdc++-v3/include/std/tuple
> > index aaee0b8826a..b82cdf12569 100644
> > --- a/libstdc++-v3/include/std/tuple
> > +++ b/libstdc++-v3/include/std/tuple
> > @@ -1344,36 +1344,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >      struct tuple_size<tuple<_Elements...>>
> >      : public integral_constant<size_t, sizeof...(_Elements)> { };
> >
> > -#if __cplusplus > 201402L
> > -  template <typename _Tp>
> > -    inline constexpr size_t tuple_size_v = tuple_size<_Tp>::value;
> > +#if __cplusplus >= 201703L
> > +  template<typename... _Types>
> > +    inline constexpr size_t tuple_size_v<tuple<_Types>>
>
> Missing ... after _Types?
>
> > +      = sizeof...(_Types);
> > +
> > +  template<typename... _Types>
> > +    inline constexpr size_t tuple_size_v<const tuple<_Types>>
>
> Same here.  (I guess now's a good time to fix PR100652!)
>
>
>
Oops!  Fixed by the attached patch, pushed to trunk. Thanks.
commit 70d6f6e41f77269f7d293c6fbccf978680e68d2a
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Nov 5 21:40:58 2021

    libstdc++: Fix pack expansions in tuple_size_v specializations
    
    libstdc++-v3/ChangeLog:
    
            * include/std/tuple (tuple_size_v): Fix pack expansion.

diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index 46173935b64..36dc05d97bc 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -1346,11 +1346,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #if __cplusplus >= 201703L
   template<typename... _Types>
-    inline constexpr size_t tuple_size_v<tuple<_Types>>
+    inline constexpr size_t tuple_size_v<tuple<_Types...>>
       = sizeof...(_Types);
 
   template<typename... _Types>
-    inline constexpr size_t tuple_size_v<const tuple<_Types>>
+    inline constexpr size_t tuple_size_v<const tuple<_Types...>>
       = sizeof...(_Types);
 #endif
diff mbox series

Patch

diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 5b400daf97f..6081e0c7fe9 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -771,6 +771,14 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct tuple_element<1, pair<_Tp1, _Tp2>>
     { typedef _Tp2 type; };
 
+#if __cplusplus >= 201703L
+  template<typename _Tp1, typename _Tp2>
+    inline constexpr size_t tuple_size_v<pair<_Tp1, _Tp2>> = 2;
+
+  template<typename _Tp1, typename _Tp2>
+    inline constexpr size_t tuple_size_v<const pair<_Tp1, _Tp2>> = 2;
+#endif
+
   /// @cond undocumented
   template<size_t _Int>
     struct __pair_get;
diff --git a/libstdc++-v3/include/bits/utility.h b/libstdc++-v3/include/bits/utility.h
index fce52a4530d..c9ffa008217 100644
--- a/libstdc++-v3/include/bits/utility.h
+++ b/libstdc++-v3/include/bits/utility.h
@@ -70,6 +70,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct tuple_size<const volatile __enable_if_has_tuple_size<_Tp>>
     : public tuple_size<_Tp> { };
 
+#if __cplusplus >= 201703L
+  template<typename _Tp>
+    inline constexpr size_t tuple_size_v = tuple_size<_Tp>::value;
+#endif
+
   /// Gives the type of the ith element of a given tuple type.
   template<size_t __i, typename _Tp>
     struct tuple_element;
@@ -97,8 +102,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
 #if __cplusplus >= 201402L
-// The standard says this macro and alias template should be in <tuple>
-// but we define them here, to be available in <utility> and <array> too.
+// The standard says this macro and alias template should be in <tuple> but we
+// we define them here, to be available in <array>, <utility> and <ranges> too.
+// _GLIBCXX_RESOLVE_LIB_DEFECTS
+// 3378. tuple_size_v/tuple_element_t should be available when
+//       tuple_size/tuple_element are
 #define __cpp_lib_tuple_element_t 201402L
 
   template<size_t __i, typename _Tp>
@@ -195,6 +203,45 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif // C++17
 #endif // C++14
 
+  template<size_t _Np, typename... _Types>
+    struct _Nth_type
+    { };
+
+  template<typename _Tp0, typename... _Rest>
+    struct _Nth_type<0, _Tp0, _Rest...>
+    { using type = _Tp0; };
+
+  template<typename _Tp0, typename _Tp1, typename... _Rest>
+    struct _Nth_type<1, _Tp0, _Tp1, _Rest...>
+    { using type = _Tp1; };
+
+  template<typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
+    struct _Nth_type<2, _Tp0, _Tp1, _Tp2, _Rest...>
+    { using type = _Tp2; };
+
+  template<size_t _Np, typename _Tp0, typename _Tp1, typename _Tp2,
+	   typename... _Rest>
+#if __cpp_concepts
+    requires (_Np >= 3)
+#endif
+    struct _Nth_type<_Np, _Tp0, _Tp1, _Tp2, _Rest...>
+    : _Nth_type<_Np - 3, _Rest...>
+    { };
+
+#if ! __cpp_concepts // Need additional specializations to avoid ambiguities.
+  template<typename _Tp0, typename _Tp1, typename... _Rest>
+    struct _Nth_type<0, _Tp0, _Tp1, _Rest...>
+    { using type = _Tp0; };
+
+  template<typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
+    struct _Nth_type<0, _Tp0, _Tp1, _Tp2, _Rest...>
+    { using type = _Tp0; };
+
+  template<typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
+    struct _Nth_type<1, _Tp0, _Tp1, _Tp2, _Rest...>
+    { using type = _Tp1; };
+#endif
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 
diff --git a/libstdc++-v3/include/std/array b/libstdc++-v3/include/std/array
index 3e12d35157c..413f8e2be01 100644
--- a/libstdc++-v3/include/std/array
+++ b/libstdc++-v3/include/std/array
@@ -481,6 +481,14 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       using type = _Tp;
     };
 
+#if __cplusplus >= 201703L
+  template<typename _Tp, size_t _Nm>
+    inline constexpr size_t tuple_size_v<array<_Tp, _Nm>> = _Nm;
+
+  template<typename _Tp, size_t _Nm>
+    inline constexpr size_t tuple_size_v<const array<_Tp, _Nm>> = _Nm;
+#endif
+
   template<typename _Tp, size_t _Nm>
     struct __is_tuple_like_impl<array<_Tp, _Nm>> : true_type
     { };
diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index aaee0b8826a..b82cdf12569 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -1344,36 +1344,23 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct tuple_size<tuple<_Elements...>>
     : public integral_constant<size_t, sizeof...(_Elements)> { };
 
-#if __cplusplus > 201402L
-  template <typename _Tp>
-    inline constexpr size_t tuple_size_v = tuple_size<_Tp>::value;
+#if __cplusplus >= 201703L
+  template<typename... _Types>
+    inline constexpr size_t tuple_size_v<tuple<_Types>>
+      = sizeof...(_Types);
+
+  template<typename... _Types>
+    inline constexpr size_t tuple_size_v<const tuple<_Types>>
+      = sizeof...(_Types);
 #endif
 
-  /**
-   * Recursive case for tuple_element: strip off the first element in
-   * the tuple and retrieve the (i-1)th element of the remaining tuple.
-   */
-  template<size_t __i, typename _Head, typename... _Tail>
-    struct tuple_element<__i, tuple<_Head, _Tail...> >
-    : tuple_element<__i - 1, tuple<_Tail...> > { };
-
-  /**
-   * Basis case for tuple_element: The first element is the one we're seeking.
-   */
-  template<typename _Head, typename... _Tail>
-    struct tuple_element<0, tuple<_Head, _Tail...> >
+  /// Trait to get the Ith element type from a tuple.
+  template<size_t __i, typename... _Types>
+    struct tuple_element<__i, tuple<_Types...>>
     {
-      typedef _Head type;
-    };
+      static_assert(__i < sizeof...(_Types), "tuple index must be in range");
 
-  /**
-   * Error case for tuple_element: invalid index.
-   */
-  template<size_t __i>
-    struct tuple_element<__i, tuple<>>
-    {
-      static_assert(__i < tuple_size<tuple<>>::value,
-	  "tuple index must be in range");
+      using type = typename _Nth_type<__i, _Types...>::type;
     };
 
   template<size_t __i, typename _Head, typename... _Tail>
diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index c4c307b7bb2..993ce3dba91 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -54,51 +54,6 @@  namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
-namespace __detail
-{
-namespace __variant
-{
-  template<size_t _Np, typename... _Types>
-    struct _Nth_type;
-
-  template<typename _Tp0, typename... _Rest>
-    struct _Nth_type<0, _Tp0, _Rest...>
-    { using type = _Tp0; };
-
-  template<typename _Tp0, typename _Tp1, typename... _Rest>
-    struct _Nth_type<1, _Tp0, _Tp1, _Rest...>
-    { using type = _Tp1; };
-
-  template<typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
-    struct _Nth_type<2, _Tp0, _Tp1, _Tp2, _Rest...>
-    { using type = _Tp2; };
-
-  template<size_t _Np, typename _Tp0, typename _Tp1, typename _Tp2,
-	   typename... _Rest>
-#if __cpp_concepts
-    requires (_Np >= 3)
-#endif
-    struct _Nth_type<_Np, _Tp0, _Tp1, _Tp2, _Rest...>
-    : _Nth_type<_Np - 3, _Rest...>
-    { };
-
-#if ! __cpp_concepts // Need additional specializations to avoid ambiguities.
-  template<typename _Tp0, typename _Tp1, typename... _Rest>
-    struct _Nth_type<0, _Tp0, _Tp1, _Rest...>
-    { using type = _Tp0; };
-
-  template<typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
-    struct _Nth_type<0, _Tp0, _Tp1, _Tp2, _Rest...>
-    { using type = _Tp0; };
-
-  template<typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
-    struct _Nth_type<1, _Tp0, _Tp1, _Tp2, _Rest...>
-    { using type = _Tp1; };
-#endif
-
-} // namespace __variant
-} // namespace __detail
-
 #if __cplusplus >= 202002L && __cpp_concepts
 // P2231R1 constexpr needs constexpr unions and constrained destructors.
 # define __cpp_lib_variant 202106L
@@ -145,8 +100,7 @@  namespace __variant
     {
       static_assert(_Np < sizeof...(_Types));
 
-      using type
-	= typename __detail::__variant::_Nth_type<_Np, _Types...>::type;
+      using type = typename _Nth_type<_Np, _Types...>::type;
     };
 
   template<size_t _Np, typename _Variant>
@@ -1442,8 +1396,7 @@  namespace __variant
 	  = __detail::__variant::__accepted_index<_Tp, variant>::value;
 
       template<size_t _Np, typename = enable_if_t<(_Np < sizeof...(_Types))>>
-	using __to_type
-	  = typename __detail::__variant::_Nth_type<_Np, _Types...>::type;
+	using __to_type = typename _Nth_type<_Np, _Types...>::type;
 
       template<typename _Tp, typename = enable_if_t<__not_self<_Tp>>>
 	using __accepted_type = __to_type<__accepted_index<_Tp>>;
@@ -1580,7 +1533,7 @@  namespace __variant
 	emplace(_Args&&... __args)
 	{
 	  namespace __variant = std::__detail::__variant;
-	  using type = typename __variant::_Nth_type<_Np, _Types...>::type;
+	  using type = typename _Nth_type<_Np, _Types...>::type;
 	  // Provide the strong exception-safety guarantee when possible,
 	  // to avoid becoming valueless.
 	  if constexpr (is_nothrow_constructible_v<type, _Args...>)
@@ -1620,7 +1573,7 @@  namespace __variant
 	emplace(initializer_list<_Up> __il, _Args&&... __args)
 	{
 	  namespace __variant = std::__detail::__variant;
-	  using type = typename __variant::_Nth_type<_Np, _Types...>::type;
+	  using type = typename _Nth_type<_Np, _Types...>::type;
 	  // Provide the strong exception-safety guarantee when possible,
 	  // to avoid becoming valueless.
 	  if constexpr (is_nothrow_constructible_v<type,
@@ -1803,8 +1756,7 @@  namespace __variant
 	  constexpr size_t __max = 11; // "These go to eleven."
 
 	  // The type of the first variant in the pack.
-	  using _V0
-	    = typename __detail::__variant::_Nth_type<0, _Variants...>::type;
+	  using _V0 = typename _Nth_type<0, _Variants...>::type;
 	  // The number of alternatives in that first variant.
 	  constexpr auto __n = variant_size_v<remove_reference_t<_V0>>;
 
diff --git a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
index 225bb6245a6..113a7fd62de 100644
--- a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
@@ -60,3 +60,4 @@  test03()
 }
 
 // { dg-error "tuple index must be in range" "" { target *-*-* } 0 }
+// { dg-prune-output "no type named 'type' in .*_Nth_type" }