20 General utilities library [utilities]

20.4 Tuples [tuple]

20.4.1 In general [tuple.general]

This subclause describes the tuple library that provides a tuple type as the class template tuple that can be instantiated with any number of arguments. Each template argument specifies the type of an element in the tuple. Consequently, tuples are heterogeneous, fixed-size collections of values. An instantiation of tuple with two arguments is similar to an instantiation of pair with the same two arguments. See [pairs].

Header <tuple> synopsis

namespace std {
  // [tuple.tuple], class template tuple:
  template <class... Types> class tuple;

  // [tuple.creation], tuple creation functions:
  const unspecified ignore;

  template <class... Types>
    constexpr tuple<VTypes...> make_tuple(Types&&...);
  template <class... Types>
    constexpr tuple<Types&&...> forward_as_tuple(Types&&...) noexcept;

  template<class... Types>
    constexpr tuple<Types&...> tie(Types&...) noexcept;

  template <class... Tuples>
    constexpr tuple<Ctypes...> tuple_cat(Tuples&&...);

  // [tuple.helper], tuple helper classes:
  template <class T> class tuple_size;  // undefined
  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>;

  template <class... Types> class tuple_size<tuple<Types...> >;

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

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

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

  // [tuple.elem], element access:
  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;
  template <size_t I, class... Types>
    constexpr const tuple_element_t<I, tuple<Types...>>&
      get(const tuple<Types...>&) noexcept;
  template <class T, class... Types>
    constexpr T& get(tuple<Types...>& t) noexcept;
  template <class T, class... Types>
    constexpr T&& get(tuple<Types...>&& t) noexcept;
  template <class T, class... Types>
    constexpr const T& get(const tuple<Types...>& t) noexcept;

  // [tuple.rel], relational operators:
  template<class... TTypes, class... UTypes>
    constexpr bool operator==(const tuple<TTypes...>&, const tuple<UTypes...>&);
  template<class... TTypes, class... UTypes>
    constexpr bool operator<(const tuple<TTypes...>&, const tuple<UTypes...>&);
  template<class... TTypes, class... UTypes>
    constexpr bool operator!=(const tuple<TTypes...>&, const tuple<UTypes...>&);
  template<class... TTypes, class... UTypes>
    constexpr bool operator>(const tuple<TTypes...>&, const tuple<UTypes...>&);
  template<class... TTypes, class... UTypes>
    constexpr bool operator<=(const tuple<TTypes...>&, const tuple<UTypes...>&);
  template<class... TTypes, class... UTypes>
    constexpr bool operator>=(const tuple<TTypes...>&, const tuple<UTypes...>&);

  // [tuple.traits], allocator-related traits
  template <class... Types, class Alloc>
    struct uses_allocator<tuple<Types...>, Alloc>;

  // [tuple.special], specialized algorithms:
  template <class... Types>
    void swap(tuple<Types...>& x, tuple<Types...>& y) noexcept(see below);
}

20.4.2 Class template tuple [tuple.tuple]

namespace std {
  template <class... Types>
  class tuple  {
  public:

    // [tuple.cnstr], tuple construction
    constexpr tuple();
    constexpr explicit tuple(const Types&...);
    template <class... UTypes>
      constexpr explicit tuple(UTypes&&...);

    tuple(const tuple&) = default;
    tuple(tuple&&) = default;

    template <class... UTypes>
      constexpr tuple(const tuple<UTypes...>&);
    template <class... UTypes>
      constexpr tuple(tuple<UTypes...>&&);

    template <class U1, class U2>
      constexpr tuple(const pair<U1, U2>&);       // only if sizeof...(Types) == 2
    template <class U1, class U2>
      constexpr tuple(pair<U1, U2>&&);            // only if sizeof...(Types) == 2

    // allocator-extended constructors
    template <class Alloc>
      tuple(allocator_arg_t, const Alloc& a);
    template <class Alloc>
      tuple(allocator_arg_t, const Alloc& a, const Types&...);
    template <class Alloc, class... UTypes>
      tuple(allocator_arg_t, const Alloc& a, UTypes&&...);
    template <class Alloc>
      tuple(allocator_arg_t, const Alloc& a, const tuple&);
    template <class Alloc>
      tuple(allocator_arg_t, const Alloc& a, tuple&&);
    template <class Alloc, class... UTypes>
      tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&);
    template <class Alloc, class... UTypes>
      tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&&);
    template <class Alloc, class U1, class U2>
      tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&);
    template <class Alloc, class U1, class U2>
      tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&&);

    // [tuple.assign], tuple assignment
    tuple& operator=(const tuple&);
    tuple& operator=(tuple&&) noexcept(see below);

    template <class... UTypes>
      tuple& operator=(const tuple<UTypes...>&);
    template <class... UTypes>
      tuple& operator=(tuple<UTypes...>&&);

    template <class U1, class U2>
      tuple& operator=(const pair<U1, U2>&);    // only if sizeof...(Types) == 2
    template <class U1, class U2>
      tuple& operator=(pair<U1, U2>&&);         // only if sizeof...(Types) == 2

    // [tuple.swap], tuple swap
    void swap(tuple&) noexcept(see below);
  };
}

20.4.2.1 Construction [tuple.cnstr]

For each tuple constructor, an exception is thrown only if the construction of one of the types in Types throws an exception.

The defaulted move and copy constructor, respectively, of tuple shall be a constexpr function if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for a constexpr function. The defaulted move and copy constructor of tuple<> shall be constexpr functions.

In the constructor descriptions that follow, let i be in the range [0,sizeof...(Types)) in order, Ti be the ith type in Types, and Ui be the ith type in a template parameter pack named UTypes, where indexing is zero-based.

constexpr tuple();

Requires: is_default_constructible<Ti>::value is true for all i.

Effects: Value initializes each element.

constexpr explicit tuple(const Types&...);

Requires: is_copy_constructible<Ti>::value is true for all i.

Effects: Initializes each element with the value of the corresponding parameter.

template <class... UTypes> constexpr explicit tuple(UTypes&&... u);

Requires: sizeof...(Types) == sizeof...(UTypes). is_constructible<Ti, Ui&&>::value is true for all i.

Effects: Initializes the elements in the tuple with the corresponding value in std::forward<UTypes>(u).

Remark: This constructor shall not participate in overload resolution unless each type in UTypes is implicitly convertible to its corresponding type in Types.

tuple(const tuple& u) = default;

Requires: is_copy_constructible<Ti>::value is true for all i.

Effects: Initializes each element of *this with the corresponding element of u.

tuple(tuple&& u) = default;

Requires: is_move_constructible<Ti>::value is true for all i.

Effects: For all i, initializes the ith element of *this with std::forward<Ti>(get<i>(u)).

template <class... UTypes> constexpr tuple(const tuple<UTypes...>& u);

Requires: sizeof...(Types) == sizeof...(UTypes). is_constructible<Ti, const Ui&>::value is true for all i.

Effects: Constructs each element of *this with the corresponding element of u.

Remark: This constructor shall not participate in overload resolution unless const Ui& is implicitly convertible to Ti for all i.

template <class... UTypes> constexpr tuple(tuple<UTypes...>&& u);

Requires: sizeof...(Types) == sizeof...(UTypes). is_constructible<Ti, Ui&&>::value is true for all i.

Effects: For all i, initializes the ith element of *this with std::forward<Ui>(get<i>(u)).

Remark: This constructor shall not participate in overload resolution unless each type in UTypes is implicitly convertible to its corresponding type in Types.

template <class U1, class U2> constexpr tuple(const pair<U1, U2>& u);

Requires: sizeof...(Types) == 2. is_constructible<T0, const U1&>::value is true for the first type T0 in Types and is_constructible<T1, const U2&>::value is true for the second type T1 in Types.

Effects: Constructs the first element with u.first and the second element with u.second.

Remark: This constructor shall not participate in overload resolution unless const U1& is implicitly convertible to T0 and const U2& is implicitly convertible to T1.

template <class U1, class U2> constexpr tuple(pair<U1, U2>&& u);

Requires: sizeof...(Types) == 2. is_constructible<T0, U1&&>::value is true for the first type T0 in Types and is_constructible<T1, U2&&>::value is true for the second type T1 in Types.

Effects: Initializes the first element with std::forward<U1>(u.first) and the second element with std::forward<U2>(u.second).

Remark: This constructor shall not participate in overload resolution unless U1 is implicitly convertible to T0 and U2 is implicitly convertible to T1.

template <class Alloc> tuple(allocator_arg_t, const Alloc& a); template <class Alloc> tuple(allocator_arg_t, const Alloc& a, const Types&...); template <class Alloc, class... UTypes> tuple(allocator_arg_t, const Alloc& a, UTypes&&...); template <class Alloc> tuple(allocator_arg_t, const Alloc& a, const tuple&); template <class Alloc> tuple(allocator_arg_t, const Alloc& a, tuple&&); template <class Alloc, class... UTypes> tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&); template <class Alloc, class... UTypes> tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&&); template <class Alloc, class U1, class U2> tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&); template <class Alloc, class U1, class U2> tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&&);

Requires: Alloc shall meet the requirements for an Allocator ([allocator.requirements]).

Effects: Equivalent to the preceding constructors except that each element is constructed with uses-allocator construction ([allocator.uses.construction]).

20.4.2.2 Assignment [tuple.assign]

For each tuple assignment operator, an exception is thrown only if the assignment of one of the types in Types throws an exception. In the function descriptions that follow, let i be in the range [0,sizeof...(Types)) in order, Ti be the ith type in Types, and Ui be the ith type in a template parameter pack named UTypes, where indexing is zero-based.

tuple& operator=(const tuple& u);

Requires: is_copy_assignable<Ti>::value is true for all i.

Effects: Assigns each element of u to the corresponding element of *this.

Returns: *this

tuple& operator=(tuple&& u) noexcept(see below);

Remark: The expression inside noexcept is equivalent to the logical and of the following expressions:

is_nothrow_move_assignable<Ti>::value

where Ti is the ith type in Types.

Requires: is_move_assignable<Ti>::value is true for all i.

Effects: For all i, assigns std::forward<Ti>(get<i>(u)) to get<i>(*this).

Returns: *this.

template <class... UTypes> tuple& operator=(const tuple<UTypes...>& u);

Requires: sizeof...(Types) == sizeof...(UTypes) and is_assignable<Ti&, const Ui&>::value is true for all i.

Effects: Assigns each element of u to the corresponding element of *this.

Returns: *this

template <class... UTypes> tuple& operator=(tuple<UTypes...>&& u);

Requires: is_assignable<Ti&, Ui&&>::value == true for all i. sizeof...(Types) ==
sizeof...(UTypes).

Effects: For all i, assigns std::forward<Ui>(get<i)>(u)) to get<i>(*this).

Returns: *this.

template <class U1, class U2> tuple& operator=(const pair<U1, U2>& u);

Requires: sizeof...(Types) == 2. is_assignable<T0&, const U1&>::value is true for the first type T0 in Types and is_assignable<T1&, const U2&>::value is true for the second type T1 in Types.

Effects: Assigns u.first to the first element of *this and u.second to the second element of *this.

Returns: *this

template <class U1, class U2> tuple& operator=(pair<U1, U2>&& u);

Requires: sizeof...(Types) == 2. is_assignable<T0&, U1&&>::value is true for the first type T0 in Types and is_assignable<T1&, U2&&>::value is true for the second type T1 in Types.

Effects: Assigns std::forward<U1>(u.first) to the first element of *this and
std::forward<U2>(u.second) to the second element of *this.

Returns: *this.

20.4.2.3 swap [tuple.swap]

void swap(tuple& rhs) noexcept(see below);

Remark: The expression inside noexcept is equivalent to the logical and of the following expressions:

noexcept(swap(declval<Ti&>>(), declval<Ti&>()))

where Ti is the ith type in Types.

Requires: Each element in *this shall be swappable with ([swappable.requirements]) the corresponding element in rhs.

Effects: Calls swap for each element in *this and its corresponding element in rhs.

Throws: Nothing unless one of the element-wise swap calls throws an exception.

20.4.2.4 Tuple creation functions [tuple.creation]

In the function descriptions that follow, let i be in the range [0,sizeof...(TTypes)) in order and let Ti be the ith type in a template parameter pack named TTypes; let j be in the range [0,sizeof...(UTypes)) in order and Uj be the jth type in a template parameter pack named UTypes, where indexing is zero-based.

template<class... Types> constexpr tuple<VTypes...> make_tuple(Types&&... t);

Let Ui be decay_t<Ti> for each Ti in Types. Then each Vi in VTypes is X& if Ui equals reference_wrapper<X>, otherwise Vi is Ui.

Returns: tuple<VTypes...>(std::forward<Types>(t)...).

Example:

int i; float j;
make_tuple(1, ref(i), cref(j))

creates a tuple of type

tuple<int, int&, const float&>

 — end example ]

template<class... Types> constexpr tuple<Types&&...> forward_as_tuple(Types&&... t) noexcept;

Effects: Constructs a tuple of references to the arguments in t suitable for forwarding as arguments to a function. Because the result may contain references to temporary variables, a program shall ensure that the return value of this function does not outlive any of its arguments. (e.g., the program should typically not store the result in a named variable).

Returns: tuple<Types&&...>(std::forward<Types>(t)...)

template<class... Types> constexpr tuple<Types&...> tie(Types&... t) noexcept;

Returns: tuple<Types&...>(t...). When an argument in t is ignore, assigning any value to the corresponding tuple element has no effect.

Example: tie functions allow one to create tuples that unpack tuples into variables. ignore can be used for elements that are not needed:

int i; std::string s;
tie(i, ignore, s) = make_tuple(42, 3.14, "C++");
// i == 42, s == "C++"

 — end example ]

template <class... Tuples> constexpr tuple<CTypes...> tuple_cat(Tuples&&... tpls);

In the following paragraphs, let Ti be the ith type in Tuples, Ui be remove_reference_t<Ti>, and tpi be the ith parameter in the function parameter pack tpls, where all indexing is zero-based.

Requires: For all i, Ui shall be the type cvi tuple<Argsi...>, where cvi is the (possibly empty) ith cv-qualifier-seq and Argsi is the parameter pack representing the element types in Ui. Let Aik be the kith type in Argsi. For all Aik the following requirements shall be satisfied: If Ti is deduced as an lvalue reference type, then is_constructible<Aik, cvi Aik&>::value == true, otherwise is_constructible<Aik, cvi Aik&&>::value == true.

Remarks: The types in Ctypes shall be equal to the ordered sequence of the extended types Args0..., Args1..., ... Argsn-1..., where n is equal to sizeof...(Tuples). Let ei... be the ith ordered sequence of tuple elements of the resulting tuple object corresponding to the type sequence Argsi.

Returns: A tuple object constructed by initializing the kith type element eik in ei... with
get<ki>(std::forward<Ti>(tpi)) for each valid ki and each group ei in order.

Note: An implementation may support additional types in the parameter pack Tuples that support the tuple-like protocol, such as pair and array.

20.4.2.5 Tuple helper classes [tuple.helper]

template <class T> struct tuple_size;

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

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

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

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

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

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>;

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

integral_constant<size_t, TS::value>

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

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

  • for the first specialization, add_const_t<TE::type>,

  • for the second specialization, add_volatile_t<TE::type>, and

  • for the third specialization, add_cv_t<TE::type>.

20.4.2.6 Element access [tuple.elem]

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

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

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

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

Effects: Equivalent to return std::forward<typename tuple_element<I, tuple<Types...> >
::type&&>(get<I>(t));

Note: if a T in Types is some reference type X&, the return type is X&, not X&&. However, if the element type is a non-reference type T, the return type is T&&.

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

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

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

Note: Constness is shallow. If a T in Types is some reference type X&, the return type is X&, not const X&. However, if the element type is non-reference type T, the return type is const T&. This is consistent with how constness is defined to work for member variables of reference type.  — end note ]

template <class T, class... Types> constexpr T& get(tuple<Types...>& t) noexcept; template <class T, class... Types> constexpr T&& get(tuple<Types...>&& t) noexcept; template <class T, class... Types> constexpr const T& get(const tuple<Types...>& t) noexcept;

Requires: The type T occurs exactly once in Types.... Otherwise, the program is ill-formed.

Returns: A reference to the element of t corresponding to the type T in Types....

Example:

  const tuple<int, const int, double, double> t(1, 2, 3.4, 5.6);
  const int &i1 = get<int>(t);        // OK. Not ambiguous. i1 == 1
  const int &i2 = get<const int>(t);  // OK. Not ambiguous. i2 == 2
  const double &d = get<double>(t);   // ERROR. ill-formed

 — end example ]

Note: The reason get is a nonmember function is that if this functionality had been provided as a member function, code where the type depended on a template parameter would have required using the template keyword.  — end note ]

20.4.2.7 Relational operators [tuple.rel]

template<class... TTypes, class... UTypes> constexpr bool operator==(const tuple<TTypes...>& t, const tuple<UTypes...>& u);

Requires: For all i, where 0 <= i and i < sizeof...(TTypes), get<i>(t) == get<i>(u) is a valid expression returning a type that is convertible to bool. sizeof...(TTypes) == sizeof...(UTypes).

Returns: true if get<i>(t) == get<i>(u) for all i, otherwise false. For any two zero-length tuples e and f, e == f returns true.

Effects: The elementary comparisons are performed in order from the zeroth index upwards. No comparisons or element accesses are performed after the first equality comparison that evaluates to false.

template<class... TTypes, class... UTypes> constexpr bool operator<(const tuple<TTypes...>& t, const tuple<UTypes...>& u);

Requires: For all i, where 0 <= i and i < sizeof...(TTypes), get<i>(t) < get<i>(u) and get<i>(u) < get<i>(t) are valid expressions returning types that are convertible to bool. sizeof...(TTypes) == sizeof...(UTypes).

Returns: The result of a lexicographical comparison between t and u. The result is defined as: (bool)(get<0>(t) < get<0>(u)) || (!(bool)(get<0>(u) < get<0>(t)) && ttail < utail), where rtail for some tuple r is a tuple containing all but the first element of r. For any two zero-length tuples e and f, e < f returns false.

template<class... TTypes, class... UTypes> constexpr bool operator!=(const tuple<TTypes...>& t, const tuple<UTypes...>& u);

Returns: !(t == u).

template<class... TTypes, class... UTypes> constexpr bool operator>(const tuple<TTypes...>& t, const tuple<UTypes...>& u);

Returns: u < t.

template<class... TTypes, class... UTypes> constexpr bool operator<=(const tuple<TTypes...>& t, const tuple<UTypes...>& u);

Returns: !(u < t)

template<class... TTypes, class... UTypes> constexpr bool operator>=(const tuple<TTypes...>& t, const tuple<UTypes...>& u);

Returns: !(t < u)

Note: The above definitions for comparison operators do not require ttail (or utail) to be constructed. It may not even be possible, as t and u are not required to be copy constructible. Also, all comparison operators are short circuited; they do not perform element accesses beyond what is required to determine the result of the comparison.  — end note ]

20.4.2.8 Tuple traits [tuple.traits]

template <class... Types, class Alloc> struct uses_allocator<tuple<Types...>, Alloc> : true_type { };

Requires: Alloc shall be an Allocator ([allocator.requirements]).

Note: Specialization of this trait informs other library components that tuple can be constructed with an allocator, even though it does not have a nested allocator_type.  — end note ]

20.4.2.9 Tuple specialized algorithms [tuple.special]

template <class... Types> void swap(tuple<Types...>& x, tuple<Types...>& y) noexcept(see below);

Remark: The expression inside noexcept is equivalent to:

noexcept(x.swap(y))

Effects: x.swap(y)