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

Section: 22.4.2 [tuple.syn], 22.4.7 [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:

22.4.7 [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. 22.4.7 [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 22.4.2 [tuple.syn], header <tuple> synopsis, as indicated:

    namespace std {
      […]
    
      // 22.4.7 [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;
        
      // 22.4.8 [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;
      […]
      
      // 22.4.7 [tuple.helper], tuple helper classes
      template<class T>
        inline constexpr size_t tuple_size_v = tuple_size<T>::value;
    }
    
  2. Modify 22.4.7 [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> (23.3.2 [array.syn]), <ranges> (25.2 [ranges.syn]), <span> (23.7.2.1 [span.syn]), or <utility> (22.2.1 [utility.syn]) are included.

    template<class T> struct tuple_size;
    

    -1- Remarks: All specializations of tuple_size shall meet the Cpp17UnaryTypeTrait requirements (21.3.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 (21.3.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> (23.3.2 [array.syn]), <ranges> (25.2 [ranges.syn]), <span> (23.7.2.1 [span.syn]), or <utility> (22.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 (21.3.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> (23.3.2 [array.syn]), <ranges> (25.2 [ranges.syn]), <span> (23.7.2.1 [span.syn]), or <utility> (22.2.1 [utility.syn]) are included.

  3. Modify 22.4.8 [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;
    […]
    

    […]