19 General utilities library [utilities]

19.5 Tuples [tuple]

19.5.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.

19.5.2 Header <tuple> synopsis [tuple.syn]

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

  // [tuple.creation], tuple creation functions
  inline constexpr unspecified ignore;

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

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

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

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

  // [tuple.apply], calling a function with a tuple of arguments
  template<class F, class Tuple>
    constexpr decltype(auto) apply(F&& f, Tuple&& t);

  template<class T, class Tuple>
    constexpr T make_from_tuple(Tuple&& t);

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

  // [tuple.helper], tuple helper classes
  template<class T>
    inline constexpr size_t tuple_size_v = tuple_size<T>::value;
}

19.5.3 Class template tuple [tuple.tuple]

namespace std {
  template<class... Types>
  class tuple {
  public:
    // [tuple.cnstr], tuple construction
    explicit(see below) constexpr tuple();
    explicit(see below) constexpr tuple(const Types&...);          // only if sizeof...(Types) >= 1
    template<class... UTypes>
      explicit(see below) constexpr tuple(UTypes&&...);            // only if sizeof...(Types) >= 1

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

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

    template<class U1, class U2>
      explicit(see below) constexpr tuple(const pair<U1, U2>&);    // only if sizeof...(Types) == 2
    template<class U1, class U2>
      explicit(see below) 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>
      explicit(see below) tuple(allocator_arg_t, const Alloc& a, const Types&...);
    template<class Alloc, class... UTypes>
      explicit(see below) 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>
      explicit(see below) tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&);
    template<class Alloc, class... UTypes>
      explicit(see below) tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&&);
    template<class Alloc, class U1, class U2>
      explicit(see below) tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&);
    template<class Alloc, class U1, class U2>
      explicit(see below) 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);
  };

  template<class... UTypes>
    tuple(UTypes...) -> tuple<UTypes...>;
  template<class T1, class T2>
    tuple(pair<T1, T2>) -> tuple<T1, T2>;
  template<class Alloc, class... UTypes>
    tuple(allocator_arg_t, Alloc, UTypes...) -> tuple<UTypes...>;
  template<class Alloc, class T1, class T2>
    tuple(allocator_arg_t, Alloc, pair<T1, T2>) -> tuple<T1, T2>;
  template<class Alloc, class... UTypes>
    tuple(allocator_arg_t, Alloc, tuple<UTypes...>) -> tuple<UTypes...>;
}

19.5.3.1 Construction [tuple.cnstr]

In the descriptions that follow, let i be in the range [0, sizeof...(Types)) in order, T be the type in Types, and U be the type in a template parameter pack named UTypes, where indexing is zero-based.
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.
If is_­trivially_­destructible_­v<T> is true for all T, then the destructor of tuple is trivial.
explicit(see below) constexpr tuple();
Effects: Value-initializes each element.
Remarks: This constructor shall not participate in overload resolution unless is_­default_­constructible_­v<T> is true for all i.
[ Note
:
This behavior can be implemented by a constructor template with default template arguments.
— end note
 ]
The expression inside explicit evaluates to true if and only if T is not implicitly default-constructible for at least one i.
[ Note
:
This behavior can be implemented with a trait that checks whether a const T& can be initialized with {}.
— end note
 ]
explicit(see below) constexpr tuple(const Types&...);
Effects: Initializes each element with the value of the corresponding parameter.
Remarks: This constructor shall not participate in overload resolution unless sizeof...(Types) >= 1 and is_­copy_­constructible_­v<T> is true for all i.
The expression inside explicit is equivalent to:
!conjunction_v<is_convertible<const Types&, Types>...>
template<class... UTypes> explicit(see below) constexpr tuple(UTypes&&... u);
Effects: Initializes the elements in the tuple with the corresponding value in std​::​forward<UTypes>(u).
Remarks: This constructor shall not participate in overload resolution unless sizeof...(Types) == sizeof...(UTypes) and sizeof...(Types) >= 1 and is_­constructible_­v<T, U&&> is true for all i.
The expression inside explicit is equivalent to:
!conjunction_v<is_convertible<UTypes, Types>...>
tuple(const tuple& u) = default;
Requires: is_­copy_­constructible_­v<T> 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_­v<T> is true for all i.
Effects: For all i, initializes the element of *this with std​::​forward<T>(get<i>(u)).
template<class... UTypes> explicit(see below) constexpr tuple(const tuple<UTypes...>& u);
Effects: Initializes each element of *this with the corresponding element of u.
Remarks: This constructor shall not participate in overload resolution unless
  • sizeof...(Types) == sizeof...(UTypes) and
  • is_­constructible_­v<T, const U&> is true for all i, and
  • either sizeof...(Types) != 1, or (when Types... expands to T and UTypes... expands to U) is_­convertible_­v<const tuple<U>&, T>, is_­constructible_­v<T, const tuple<U>&>, and is_­same_­v<T, U> are all false.
The expression inside explicit is equivalent to:
!conjunction_v<is_convertible<const UTypes&, Types>...>
template<class... UTypes> explicit(see below) constexpr tuple(tuple<UTypes...>&& u);
Effects: For all i, initializes the element of *this with std​::​forward<U>(get<i>(u)).
Remarks: This constructor shall not participate in overload resolution unless
  • sizeof...(Types) == sizeof...(UTypes), and
  • is_­constructible_­v<T, U&&> is true for all i, and
  • either sizeof...(Types) != 1, or (when Types... expands to T and UTypes... expands to U) is_­convertible_­v<tuple<U>, T>, is_­constructible_­v<T, tuple<U>>, and is_­same_­v<T, U> are all false.
The expression inside explicit is equivalent to:
!conjunction_v<is_convertible<UTypes, Types>...>
template<class U1, class U2> explicit(see below) constexpr tuple(const pair<U1, U2>& u);
Effects: Initializes the first element with u.first and the second element with u.second.
Remarks: This constructor shall not participate in overload resolution unless sizeof...(Types) == 2, is_­constructible_­v<T, const U1&> is true and is_­constructible_­v<T, const U2&> is true.
The expression inside explicit is equivalent to:
!is_convertible_v<const U1&, T> || !is_convertible_v<const U2&, T>
template<class U1, class U2> explicit(see below) constexpr tuple(pair<U1, U2>&& u);
Effects: Initializes the first element with std​::​forward<U1>(u.first) and the second element with std​::​forward<U2>(u.second).
Remarks: This constructor shall not participate in overload resolution unless sizeof...(Types) == 2, is_­constructible_­v<T, U1&&> is true and is_­constructible_­v<T, U2&&> is true.
The expression inside explicit is equivalent to:
!is_convertible_v<U1, T> || !is_convertible_v<U2, T>
template<class Alloc> tuple(allocator_arg_t, const Alloc& a); template<class Alloc> explicit(see below) tuple(allocator_arg_t, const Alloc& a, const Types&...); template<class Alloc, class... UTypes> explicit(see below) 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> explicit(see below) tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&); template<class Alloc, class... UTypes> explicit(see below) tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&&); template<class Alloc, class U1, class U2> explicit(see below) tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&); template<class Alloc, class U1, class U2> explicit(see below) tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&&);
Requires: Alloc shall satisfy the Cpp17Allocator requirements (Table 33).
Effects: Equivalent to the preceding constructors except that each element is constructed with uses-allocator construction.

19.5.3.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, T be the type in Types, and U be the type in a template parameter pack named UTypes, where indexing is zero-based.
tuple& operator=(const tuple& u);
Effects: Assigns each element of u to the corresponding element of *this.
Remarks: This operator shall be defined as deleted unless is_­copy_­assignable_­v<T> is true for all i.
Returns: *this.
tuple& operator=(tuple&& u) noexcept(see below);
Effects: For all i, assigns std​::​forward<T>(get<i>(u)) to get<i>(*this).
Remarks: This operator shall not participate in overload resolution unless is_­move_­assignable_­v<T> is true for all i.
Remarks: The expression inside noexcept is equivalent to the logical and of the following expressions:
is_nothrow_move_assignable_v<>
where is the type in Types.
Returns: *this.
template<class... UTypes> tuple& operator=(const tuple<UTypes...>& u);
Effects: Assigns each element of u to the corresponding element of *this.
Remarks: This operator shall not participate in overload resolution unless sizeof...(Types) == sizeof...(UTypes) and is_­assignable_­v<T&, const U&> is true for all i.
Returns: *this.
template<class... UTypes> tuple& operator=(tuple<UTypes...>&& u);
Effects: For all i, assigns std​::​forward<U>(get<i>(u)) to get<i>(*this).
Remarks: This operator shall not participate in overload resolution unless is_­assignable_­v<T&, U&&> == true for all i and sizeof...(Types) == sizeof...(UTypes).
Returns: *this.
template<class U1, class U2> tuple& operator=(const pair<U1, U2>& u);
Effects: Assigns u.first to the first element of *this and u.second to the second element of *this.
Remarks: This operator shall not participate in overload resolution unless sizeof...(Types) == 2 and is_­assignable_­v<T&, const U1&> is true for the first type T in Types and is_­assignable_­v<T&, const U2&> is true for the second type T in Types.
Returns: *this.
template<class U1, class U2> tuple& operator=(pair<U1, U2>&& u);
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.
Remarks: This operator shall not participate in overload resolution unless sizeof...(Types) == 2 and is_­assignable_­v<T&, U1&&> is true for the first type T in Types and is_­assignable_­v<T&, U2&&> is true for the second type T in Types.
Returns: *this.

19.5.3.3 swap [tuple.swap]

void swap(tuple& rhs) noexcept(see below);
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.
Remarks: The expression inside noexcept is equivalent to the logical and of the following expressions:
is_nothrow_swappable_v<>
where is the type in Types.
Throws: Nothing unless one of the element-wise swap calls throws an exception.

19.5.3.4 Tuple creation functions [tuple.creation]

In the function descriptions that follow, the members of a template parameter pack XTypes are denoted by X for i in [0, sizeof...(XTypes)) in order, where indexing is zero-based.
template<class... TTypes> constexpr tuple<VTypes...> make_tuple(TTypes&&... t);
The pack VTypes is defined as follows.
Let U be decay_­t<T> for each T in TTypes.
If U is a specialization of reference_­wrapper, then V in VTypes is U​::​type&, otherwise V is U.
Returns: tuple<VTypes...>(std​::​forward<TTypes>(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... TTypes> constexpr tuple<TTypes&&...> forward_as_tuple(TTypes&&... 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<TTypes&&...>(std​::​forward<TTypes>(t)...).
template<class... TTypes> constexpr tuple<TTypes&...> tie(TTypes&... t) noexcept;
Returns: tuple<TTypes&...>(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 T be the type in Tuples, U be remove_­reference_­t<T>, and tp be the parameter in the function parameter pack tpls, where all indexing is zero-based.
Requires: For all i, U shall be the type tuple<Args...>, where is the (possibly empty) cv-qualifier-seq and Args is the template parameter pack representing the element types in U.
Let A be the type in Args.
For all A the following requirements shall be satisfied:
  • If T is deduced as an lvalue reference type, then is_­constructible_­v<A, &> == true, otherwise
  • is_­constructible_­v<A, &&> == true.
Remarks: The types in CTypes shall be equal to the ordered sequence of the extended types Args..., Args..., , Args..., where n is equal to sizeof...(Tuples).
Let e... be the ordered sequence of tuple elements of the resulting tuple object corresponding to the type sequence Args.
Returns: A tuple object constructed by initializing the type element e in e... with
get<>(std::forward<T>(tp))
for each valid and each group e in order.
[ Note
:
An implementation may support additional types in the template parameter pack Tuples that support the tuple-like protocol, such as pair and array.
— end note
 ]

19.5.3.5 Calling a function with a tuple of arguments [tuple.apply]

template<class F, class Tuple> constexpr decltype(auto) apply(F&& f, Tuple&& t);
Effects: Given the exposition-only function:
template<class F, class Tuple, size_t... I>
constexpr decltype(auto)
    apply_impl(F&& f, Tuple&& t, index_sequence<I...>) {                // exposition only
  return INVOKE(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...);  // see [func.require]
}
Equivalent to:
return apply_impl(std::forward<F>(f), std::forward<Tuple>(t),
                  make_index_sequence<tuple_size_v<remove_reference_t<Tuple>>>{});
template<class T, class Tuple> constexpr T make_from_tuple(Tuple&& t);
Effects: Given the exposition-only function:
template<class T, class Tuple, size_t... I>
constexpr T make_from_tuple_impl(Tuple&& t, index_sequence<I...>) {     // exposition only
  return T(get<I>(std::forward<Tuple>(t))...);
}
Equivalent to:
return make_from_tuple_impl<T>(
           forward<Tuple>(t),
           make_index_sequence<tuple_size_v<remove_reference_t<Tuple>>>{});
[ Note
:
The type of T must be supplied as an explicit template parameter, as it cannot be deduced from the argument list.
— end note
 ]

19.5.3.6 Tuple helper classes [tuple.helper]

template<class T> struct tuple_size;
Remarks: All specializations of tuple_­size shall satisfy the Cpp17UnaryTypeTrait requirements ([meta.rqmts]) with a base characteristic 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: using type = TI; };
Requires: I < sizeof...(Types).
The program is ill-formed if I is out of bounds.
Type: TI is the type of the I 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.
If the expression TS​::​value is well-formed when treated as an unevaluated operand, then each of the three templates shall satisfy the Cpp17UnaryTypeTrait requirements ([meta.rqmts]) with a base characteristic of
integral_constant<size_t, TS::value>
Otherwise, they shall have no member value.
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
 ]
In addition to being available via inclusion of the <tuple> header, the three templates are available when either of the headers <array> or <utility> are included.
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_­t<I, T> of the cv-unqualified type T.
Then each of the three templates shall satisfy the Cpp17TransformationTrait requirements ([meta.rqmts]) with a member typedef type that names the following type:
  • for the first specialization, add_­const_­t<TE>,
  • for the second specialization, add_­volatile_­t<TE>, and
  • for the third specialization, add_­cv_­t<TE>.
In addition to being available via inclusion of the <tuple> header, the three templates are available when either of the headers <array> or <utility> are included.

19.5.3.7 Element access [tuple.elem]

template<size_t I, class... Types> constexpr tuple_element_t<I, tuple<Types...>>& get(tuple<Types...>& t) noexcept; template<size_t I, class... Types> constexpr tuple_element_t<I, tuple<Types...>>&& get(tuple<Types...>&& t) noexcept; // Note A template<size_t I, class... Types> constexpr const tuple_element_t<I, tuple<Types...>>& get(const tuple<Types...>& t) noexcept; // Note B template<size_t I, class... Types> constexpr const tuple_element_t<I, tuple<Types...>>&& get(const tuple<Types...>&& t) noexcept;
Requires: I < sizeof...(Types).
The program is ill-formed if I is out of bounds.
Returns: A reference to the I element of t, where indexing is zero-based.
[ Note
:
[Note A] 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&&.
— end note
 ]
[ Note
:
[Note B] 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 a 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; 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 non-member 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
 ]

19.5.3.8 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);
Returns: !(t == u).
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), both 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)) && t < u), where r 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: 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 functions do not require t (or u) to be constructed.
It may not even be possible, as t and u are not required to be copy constructible.
Also, all comparison functions are short circuited; they do not perform element accesses beyond what is required to determine the result of the comparison.
— end note
 ]

19.5.3.9 Tuple traits [tuple.traits]

template<class... Types, class Alloc> struct uses_allocator<tuple<Types...>, Alloc> : true_type { };
Requires: Alloc shall satisfy the Cpp17Allocator requirements (Table 33).
[ 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
 ]

19.5.3.10 Tuple specialized algorithms [tuple.special]

template<class... Types> void swap(tuple<Types...>& x, tuple<Types...>& y) noexcept(see below);
Remarks: This function shall not participate in overload resolution unless is_­swappable_­v<T> is true for all i, where .
The expression inside noexcept is equivalent to:
noexcept(x.swap(y))
Effects: As if by x.swap(y).