775. Tuple indexing should be unsigned?

Section: 23.5.3.6 [tuple.helper] Status: CD1 Submitter: Alisdair Meredith Opened: 2008-01-16 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [tuple.helper].

View all issues with CD1 status.

Discussion:

The tuple element access API identifies the element in the sequence using signed integers, and then goes on to enforce the requirement that I be >= 0. There is a much easier way to do this - declare I as unsigned.

In fact the proposal is to use std::size_t, matching the type used in the tuple_size API.

A second suggestion is that it is hard to imagine an API that deduces and index at compile time and returns a reference throwing an exception. Add a specific Throws: Nothing paragraph to each element access API.

In addition to tuple, update the API applies to pair and array, and should be updated accordingly.

A third observation is that the return type of the get functions for std::pair is pseudo-code, but it is not clearly marked as such. There is actually no need for pseudo-code as the return type can be specified precisely with a call to tuple_element. This is already done for std::tuple, and std::array does not have a problem as all elements are of type T.

Proposed resolution:

Update header <utility> synopsis in 23.2 [utility]

// 20.2.3, tuple-like access to pair:
template <class T> class tuple_size;
template <intsize_t I, class T> class tuple_element;

template <class T1, class T2> struct tuple_size<std::pair<T1, T2> >;
template <class T1, class T2> struct tuple_element<0, std::pair<T1, T2> >;
template <class T1, class T2> struct tuple_element<1, std::pair<T1, T2> >;

template<intsize_t I, class T1, class T2>
  Ptypename tuple_element<I, std::pair<T1, T2> >::type & get(std::pair<T1, T2>&);
template<intsize_t I, class T1, class T2>
  const Ptypename tuple_element<I, std::pair<T1, T2> >::type & get(const std::pair<T1, T2>&);

Update 23.4 [pairs] Pairs

template<intsize_t I, class T1, class T2>
  Ptypename tuple_element<I, std::pair<T1, T2> >::type & get(pair<T1, T2>&);
template<intsize_t I, class T1, class T2>
  const Ptypename tuple_element<I, std::pair<T1, T2> >::type & get(const pair<T1, T2>&);

24 Return type: If I == 0 then P is T1, if I == 1 then P is T2, and otherwise the program is ill-formed.

25 Returns: If I == 0 returns p.first, otherwise if I == 1 returns p.second, and otherwise the program is ill-formed.

Throws: Nothing.

Update header <tuple> synopsis in 23.5 [tuple] with a APIs as below:

template <intsize_t I, class T> class tuple_element; // undefined
template <intsize_t I, class... Types> class tuple_element<I, tuple<Types...> >;

// 20.3.1.4, element access:
template <intsize_t I, class... Types>
  typename tuple_element<I, tuple<Types...> >::type& get(tuple<Types...>&);
template <intsize_t I, class ... types>
  typename tuple_element<I, tuple<Types...> >::type const& get(const tuple<Types...>&);

Update 23.5.3.6 [tuple.helper] Tuple helper classes

template <intsize_t I, class... Types>
class tuple_element<I, tuple<Types...> > {
public:
  typedef TI type;
};

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

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

Update 23.5.3.7 [tuple.elem] Element access

template <intsize_t I, class... types >
typename tuple_element<I, tuple<Types...> >::type& get(tuple<Types...>& t);

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

2 Returns: A reference to the Ith element of t, where indexing is zero-based.

Throws: Nothing.
template <intsize_t I, class... types>
typename tuple_element<I, tuple<Types...> >::type const& get(const tuple<Types...>& t);

3 Requires: 0 <= I and I < sizeof...(Types). The program is ill-formed if I is out of bounds.

4 Returns: A const reference to the Ith element of t, where indexing is zero-based.

Throws: Nothing.

Update header <array> synopsis in 23.2 [utility]

template <class T> class tuple_size; // forward declaration
template <intsize_t I, class T> class tuple_element; // forward declaration
template <class T, size_t N>
  struct tuple_size<array<T, N> >;
template <intsize_t I, class T, size_t N>
  struct tuple_element<I, array<T, N> >;
template <intsize_t I, class T, size_t N>
  T& get(array<T, N>&);
template <intsize_t I, class T, size_t N>
  const T& get(const array<T, N>&);

Update 26.3.7.6 [array.tuple] Tuple interface to class template array

tuple_element<size_t I, array<T, N> >::type

3 Requires: 0 <= I < N. The program is ill-formed if I is out of bounds.

4 Value: The type T.

template <intsize_t I, class T, size_t N> T& get(array<T, N>& a);

5 Requires: 0 <= I < N. The program is ill-formed if I is out of bounds.

Returns: A reference to the Ith element of a, where indexing is zero-based.

Throws: Nothing.

template <intsize_t I, class T, size_t N> const T& get(const array<T, N>& a);

6 Requires: 0 <= I < N. The program is ill-formed if I is out of bounds.

7 Returns: A const reference to the Ith element of a, where indexing is zero-based.

Throws: Nothing.

[ Bellevue: Note also that the phrase "The program is ill-formed if I is out of bounds" in the requires clauses are probably unnecessary, and could be removed at the editor's discretion. Also std:: qualification for pair is also unnecessary. ]