20 General utilities library [utilities]

20.4 Tuples [tuple]

20.4.2 Class template tuple [tuple.tuple]

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

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

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

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

    template <class U1, class U2>
      tuple(const pair<U1, U2>&);       // iff sizeof...(Types) == 2
    template <class U1, class U2>
      tuple(pair<U1, U2>&&);            // iff 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>&);    // iff sizeof...(Types) == 2
    template <class U1, class U2>
      tuple& operator=(pair<U1, U2>&&);         // iff 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.

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.

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> 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> 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> 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> 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> 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 T1 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> tuple<VTypes...> make_tuple(Types&&... t);

Let Ui be decay<Ti>::type 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> 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> 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> tuple<CTypes...> tuple_cat(Tuples&&... tpls);

In the following paragraphs, let Ti be the ith type in Tuples, Ui be remove_reference<Ti>::type, 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... 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<remove_cv<decltype(TS::value)>::type, 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<TE::type>::type,

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

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

20.4.2.6 Element access [tuple.elem]

template <size_t I, class... Types> typename tuple_element<I, tuple<Types...> >::type& 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> typename tuple_element<I, tuple<Types...> >::type&& 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> typename tuple_element<I, tuple<Types...> >::type 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 ]

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> bool operator==(const tuple<TTypes...>& t, const tuple<UTypes...>& u);

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

Returns: true iff get<i>(t) == get<i>(u) for all i. 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> bool operator<(const tuple<TTypes...>& t, const tuple<UTypes...>& u);

Requires: For all i, where 0 <= i and i < sizeof...(Types), 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> bool operator!=(const tuple<TTypes...>& t, const tuple<UTypes...>& u);

Returns: !(t == u).

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

Returns: u < t.

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

Returns: !(u < t)

template<class... TTypes, class... UTypes> 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)