2313. tuple_size should always derive from integral_constant<size_t, N>

Section: 22.4.7 [tuple.helper] Status: C++14 Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2017-07-05

Priority: 2

View all other issues in [tuple.helper].

View all issues with C++14 status.

Discussion:

In 22.4.7 [tuple.helper], the "primary template" is depicted as:

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

However, 22.3.4 [pair.astuple]/1-2 and 23.3.3.7 [array.tuple]/1-2 are underspecified, saying:

tuple_size<pair<T1, T2> >::value

Returns: Integral constant expression.

Value: 2.

tuple_size<array<T, N> >::value

Return type: integral constant expression.

Value: N

They should be required to behave like the "primary template". This is more than a stylistic decision — it allows tuple_size to be passed to a function taking integral_constant.

LWG 1118 noticed this underspecification, but instead of correcting it, the resolution changed 22.4.7 [tuple.helper]/3 to require tuple_size<cv T> to derive from integral_constant<remove_cv<decltype(TS::value)>::type, TS::value>. This is unnecessarily overgeneralized. tuple_size is primarily for tuples, where it is required to be size_t, and it has been extended to handle pairs and arrays, which (as explained above) should also be guaranteed to be size_t. tuple_size<cv T> works with cv-qualified tuples, pairs, arrays, and user-defined types that also want to participate in the tuple_size system. It would be far simpler and perfectly reasonable to expect that user-defined types supporting the "tuple-like protocol" should have tuple_sizes of size_t.

[Issaquah 2014-02-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Edit 22.3.4 [pair.astuple]/1-2 as indicated:

    tuple_size<pair<T1, T2> >::value
    template <class T1, class T2>
    struct tuple_size<pair<T1, T2>>
      : integral_constant<size_t, 2> { };
    

    -1- Returns: Integral constant expression.

    -2- Value: 2.

  2. Edit 23.3.3.7 [array.tuple]/1-2 as indicated:

    tuple_size<array<T, N> >::value
    template <class T, size_t N>
    struct tuple_size<array<T, N>>
      : integral_constant<size_t, N> { };
    

    -1- Returns: Integral constant expression.

    -2- Value: N.

  3. Edit 22.4.7 [tuple.helper]/p1-p3 as indicated:

    template <class T> struct tuple_size;
    

    -?- Remarks: All specializations of tuple_size<T> shall meet the UnaryTypeTrait requirements (21.3.2 [meta.rqmts]) with a BaseCharacteristic of integral_constant<size_t, N> for some N.

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

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

    […]

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

    -3- Let TS denote tuple_size<T> of the cv-unqualified type T. Then each of the three templates shall meet the UnaryTypeTrait requirements (21.3.2 [meta.rqmts]) with a BaseCharacteristic of

    integral_constant<remove_cv<decltype(TS::value)>::typesize_t, TS::value>