This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

### 3378. `tuple_size_v/tuple_element_t` should be available when `tuple_size/tuple_element` are

Section: 20.5.2 [tuple.syn], 20.5.6 [tuple.helper] Status: New Submitter: Casey Carter Opened: 2020-01-17 Last modified: 2021-11-04

Priority: 3

View all issues with New status.

Discussion:

20.5.6 [tuple.helper]/6 makes the `const`/`volatile`/`const volatile` partial specializations of `tuple_size` available when any of `<array>`, `<ranges>`, `<span>`, or `<utility>` is included. 20.5.6 [tuple.helper]/8 makes the `const`/`volatile`/`const volatile` partial specializations of `tuple_element` available when any of those same headers is included. This leads to a couple of problems:

1. For users of the Standard Library, it's not helpful to have these partial specializations of class templates available when the preferred interface — the variable template `tuple_size_v` and alias template `tuple_element_t` — are not.

2. For specifiers of the Standard Library, we must update two distinct yet identical lists of headers that make this same set of templates available when adding another header.

We could solve both of these problems by coalescing the two paragraphs into one and including the variable and alias template in the set of declarations made available by the pertinent (now single) list of headers.

[2020-02-08 Issue Prioritization]

Priority to 3 after reflector discussion. Tim Song said: It’s not clear that entities only mentioned in the synopsis are "defined in this subclause".

[2021-11-04; Jonathan Wakely adds note]

The `__cpp_lib_tuple_element_t` macro in 17.3.2 [version.syn] should be updated too.

Proposed resolution:

This wording is relative to N4842.

1. Modify 20.5.2 [tuple.syn], header `<tuple>` synopsis, as indicated:

   >     namespace std {
   >       […]

   >       // 20.5.6 [tuple.helper], tuple helper classes
   >       template<class T> struct tuple_size; // not defined
   >       template<class T> struct tuple_size<const T>;
   >       template<class T> struct tuple_size<volatile T>;
   >       template<class T> struct tuple_size<const volatile T>;

   >       template<class T>
   >         inline constexpr size_t tuple_size_v = tuple_size<T>::value;
   >       template<class... Types> struct tuple_size<tuple<Types...>>;

   >       template<size_t I, class T> struct tuple_element; // not defined
   >       template<size_t I, class T> struct tuple_element<I, const T>;
   >       template<size_t I, class T> struct tuple_element<I, volatile T>;
   >       template<size_t I, class T> struct tuple_element<I, const volatile T>;

   >       template<size_t I, class... Types>
   >         struct tuple_element<I, tuple<Types...>>;

   >       template<size_t I, class T>
   >         using tuple_element_t = typename tuple_element<I, T>::type;

   >       // 20.5.7 [tuple.elem], element access
   >       template<class... Types> struct tuple_size<tuple<Types...>>;

   >       template<size_t I, class... Types>
   >         struct tuple_element<I, tuple<Types...>>;

   >       template<size_t I, class... Types>
   >         constexpr tuple_element_t<I, tuple<Types...>>& get(tuple<Types...>&) noexcept;
   >       template<size_t I, class... Types>
   >         constexpr tuple_element_t<I, tuple<Types...>>&& get(tuple<Types...>&&) noexcept;
   >       […]

   >       // 20.5.6 [tuple.helper], tuple helper classes
   >       template<class T>
   >         inline constexpr size_t tuple_size_v = tuple_size<T>::value;
   >     }

2. Modify 20.5.6 [tuple.helper] as indicated:

   > 20.5.6 Tuple helper classes [tuple.helper]

   > -?- In addition to being available via inclusion of the `<tuple>` header, the entities defined in this subclause [tuple.helper] are available when any of the headers `<array>` (22.3.2 [array.syn]), `<ranges>` (24.2 [ranges.syn]), `<span>` (22.7.2 [span.syn]), or `<utility>` (20.2.1 [utility.syn]) are included.

   >     template<class T> struct tuple_size;

   > > -1- Remarks: All specializations of `tuple_size` shall meet the Cpp17UnaryTypeTrait requirements (20.15.2 [meta.rqmts]) with a base characteristic of `integral_constant<size_t, N>` for some `N`.

   >     template<class... Types>
   >       struct tuple_size<tuple<Types...>> : public integral_constant<size_t, sizeof...(Types)> { };

   >     template<size_t I, class... Types>
   >       struct tuple_element<I, tuple<Types...>> {
   >         using type = TI;
   >       };

   > > -2- Requires: `I < sizeof...(Types)`. The program is ill-formed if `I` is out of bounds.

   > > -3- Type: `TI` is the type of the `Ith` element of `Types`, where indexing is zero-based.

   >     template<class T> struct tuple_size<const T>;
   >     template<class T> struct tuple_size<volatile T>;
   >     template<class T> struct tuple_size<const volatile T>;

   > > -4- Let `TS` denote `tuple_size<T>` of the cv-unqualified type `T`. If the expression `TS::value` is well-formed when treated as an unevaluated operand, then each of the three templates shall meet the Cpp17UnaryTypeTrait requirements (20.15.2 [meta.rqmts]) with a base characteristic of

   > > >     integral_constant<size_t, TS::value>

   > > Otherwise, they shall have no member value.

   > > -5- Access checking is performed as if in a context unrelated to `TS` and `T`. Only the validity of the immediate context of the expression is considered. [Note: The compilation of the expression can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]

   > > -6- In addition to being available via inclusion of the `<tuple>` header, the three templates are available when any of the headers `<array>` (22.3.2 [array.syn]), `<ranges>` (24.2 [ranges.syn]), `<span>` (22.7.2 [span.syn]), or `<utility>` (20.2.1 [utility.syn]) are included.

   >     template<size_t I, class T> struct tuple_element<I, const T>;
   >     template<size_t I, class T> struct tuple_element<I, volatile T>;
   >     template<size_t I, class T> struct tuple_element<I, const volatile T>;

   > > -7- Let `TE` denote `tuple_element_t<I, T>` of the cv-unqualified type `T`. Then each of the three templates shall meet the Cpp17TransformationTrait requirements (20.15.2 [meta.rqmts]) with a member typedef type that names the following type:

   > > 1. (7.1) — for the first specialization, `add_const_t<TE>`,

   > > 2. (7.2) — for the second specialization, `add_volatile_t<TE>`, and

   > > 3. (7.3) — for the third specialization, `add_cv_t<TE>`.

   > > -8- In addition to being available via inclusion of the `<tuple>` header, the three templates are available when any of the headers `<array>` (22.3.2 [array.syn]), `<ranges>` (24.2 [ranges.syn]), `<span>` (22.7.2 [span.syn]), or `<utility>` (20.2.1 [utility.syn]) are included.

3. Modify 20.5.7 [tuple.elem] as indicated:

   > [Drafting note: Since this issue performs colliding text changes with P1460R0, we perform similar wording changes as suggested on page 19 [tuple.helper] p2.]

   > 20.5.7 Element access [tuple.elem]

   >     template<class... Types>
   >       struct tuple_size<tuple<Types...>> : public integral_constant<size_t, sizeof...(Types)> { };

   >     template<size_t I, class... Types>
   >       struct tuple_element<I, tuple<Types...>> {
   >         using type = TI;
   >       };

   > > -?- Mandates: `I < sizeof...(Types)`.

   > > -?- Type: `TI` is the type of the `Ith` element of `Types`, where indexing is zero-based.

   >     template<size_t I, class... Types>
   >       constexpr tuple_element_t<I, tuple<Types...>>&
   >         get(tuple<Types...>& t) noexcept;
   >     […]

   > > […]