В этом подпункте описывается библиотека кортежей, которая предоставляет тип кортежа в качестве шаблона класса, tuple который может быть создан с любым количеством аргументов. Каждый аргумент шаблона определяет тип элемента в tuple. Следовательно, кортежи представляют собой разнородные коллекции значений фиксированного размера. Создание экземпляра tuple с двумя аргументами аналогично созданию экземпляра pair с теми же двумя аргументами. Смотрите [pairs].
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; }
namespace std { template <class... Types> class tuple { public: // [tuple.cnstr], tuple construction EXPLICIT constexpr tuple(); EXPLICIT constexpr tuple(const Types&...); // only if sizeof...(Types) >= 1 template <class... UTypes> EXPLICIT constexpr tuple(UTypes&&...); // only if sizeof...(Types) >= 1 tuple(const tuple&) = default; tuple(tuple&&) = default; template <class... UTypes> EXPLICIT constexpr tuple(const tuple<UTypes...>&); template <class... UTypes> EXPLICIT constexpr tuple(tuple<UTypes...>&&); template <class U1, class U2> EXPLICIT constexpr tuple(const pair<U1, U2>&); // only if sizeof...(Types) == 2 template <class U1, class U2> EXPLICIT 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 tuple(allocator_arg_t, const Alloc& a, const Types&...); template <class Alloc, class... UTypes> EXPLICIT 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 tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&); template <class Alloc, class... UTypes> EXPLICIT tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&&); template <class Alloc, class U1, class U2> EXPLICIT tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&); template <class Alloc, class U1, class U2> EXPLICIT 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...>; }
Для каждого tuple конструктора создается исключение, только если конструкция одного из типов в Types вызывает исключение.
Конструктор перемещения и копирования по умолчанию, соответственно, tuple должен быть функцией constexpr тогда и только тогда, когда все требуемые поэлементные инициализации для копирования и перемещения, соответственно, будут удовлетворять требованиям для функции constexpr. Конструктор перемещения и копирования по умолчанию tuple<> должен быть функциями constexpr.
Деструктор кортежа должен быть тривиальным деструктором, если (is_trivially_destructible_v<Types> && ...) есть true.
В нижеследующих описаниях конструктора пусть i будет в диапазоне [0, sizeof...(Types)) по порядку, Ti будет ith типом в Typesи Ui будет ith типом в названном пакете параметров шаблона UTypes, где индексирование отсчитывается от нуля.
EXPLICIT constexpr tuple();
Remarks: Этот конструктор не должен участвовать в разрешении перегрузки, кроме случаев, когда он is_default_constructible_v<Ti> предназначен true для всех i. [ Note: Это поведение может быть реализовано с помощью шаблона конструктора с аргументами шаблона по умолчанию. ] Конструктор является явным тогда и только тогда, когда он не может быть неявно сконструирован по умолчанию хотя бы для одного . [ Это поведение может быть реализовано с помощью трейта, который проверяет, можно ли инициализировать с помощью . ] — end note Ti i Note: const Ti& {} — end note
EXPLICIT constexpr tuple(const Types&...);
Remarks: Этот конструктор не должен участвовать в разрешении перегрузки , если sizeof...(Types) >= 1 и is_copy_constructible_v<Ti> не true для всех i. Конструктор является явным тогда и только тогда, когда is_convertible_v<const Ti&, Ti> он false предназначен хотя бы для одного i.
template <class... UTypes> EXPLICIT constexpr tuple(UTypes&&... u);
Effects: Инициализирует элементы в кортеже с соответствующим значением в std::forward<UTypes>(u).
Remarks: Этот конструктор не должен участвовать в разрешении перегрузки , если sizeof...(Types) == sizeof...(UTypes) и sizeof...(Types) >= 1 и is_constructible_v<Ti, Ui&&> является true для всех i. Конструктор является явным тогда и только тогда, когда is_convertible_v<Ui&&, Ti> он false предназначен хотя бы для одного i.
tuple(const tuple& u) = default;
tuple(tuple&& u) = default;
template <class... UTypes> EXPLICIT constexpr tuple(const tuple<UTypes...>& u);
Remarks: Этот конструктор не должен участвовать в разрешении перегрузки, если только
sizeof...(Types) == sizeof...(UTypes) а также
is_constructible_v<Ti, const Ui&> это true для всех i, и
sizeof...(Types) != 1, или (когда Types... расширяется до T и UTypes... расширяется до U)
!is_convertible_v<const tuple<U>&, T> && !is_constructible_v<T, const tuple<U>&>
&& !is_same_v<T, U>
равно true.
Конструктор является явным тогда и только тогда, когда is_convertible_v<const Ui&, Ti> он false предназначен хотя бы для одного i.
template <class... UTypes> EXPLICIT constexpr tuple(tuple<UTypes...>&& u);
Remarks: Этот конструктор не должен участвовать в разрешении перегрузки, если только
sizeof...(Types) == sizeof...(UTypes), а также
is_constructible_v<Ti, Ui&&> это true для всех i, и
sizeof...(Types) != 1, или (когда Types... расширяется до T и UTypes... расширяется до U)
!is_convertible_v<tuple<U>, T> && !is_constructible_v<T, tuple<U>> &&
!is_same_v<T, U>
равно true.
Конструктор является явным тогда и только тогда, когда is_convertible_v<Ui&&, Ti> он false предназначен хотя бы для одного i.
template <class U1, class U2> EXPLICIT constexpr tuple(const pair<U1, U2>& u);
Remarks: Этот конструктор не должен участвовать в разрешении перегрузки, за исключением случаев sizeof...(Types) == 2, когда is_constructible_v<T0, const U1&> есть true и is_constructible_v<T1, const U2&> есть true.
Конструктор является явным тогда и только тогда, когда is_convertible_v<const U1&, T0> есть false или is_convertible_v<const U2&, T1> есть false.
template <class U1, class U2> EXPLICIT constexpr tuple(pair<U1, U2>&& u);
Effects: Инициализирует первый элемент с помощью, std::forward<U1>(u.first) а второй элемент с помощью std::forward<U2>(u.second).
Remarks: Этот конструктор не должен участвовать в разрешении перегрузки, за исключением случаев sizeof...(Types) == 2, когда is_constructible_v<T0, U1&&> есть true и is_constructible_v<T1, U2&&> есть true.
Конструктор является явным тогда и только тогда, когда is_convertible_v<U1&&, T0> есть false или is_convertible_v<U2&&, T1> есть false.
template <class Alloc>
tuple(allocator_arg_t, const Alloc& a);
template <class Alloc>
EXPLICIT tuple(allocator_arg_t, const Alloc& a, const Types&...);
template <class Alloc, class... UTypes>
EXPLICIT 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 tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&);
template <class Alloc, class... UTypes>
EXPLICIT tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&&);
template <class Alloc, class U1, class U2>
EXPLICIT tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&);
template <class Alloc, class U1, class U2>
EXPLICIT tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&&);
Effects: Эквивалентен предыдущим конструкторам, за исключением того, что каждый элемент построен с использованием uses-allocator construction.
Для каждого tuple оператора присваивания исключение выдается только в том случае, если присвоение одного из типов в Types вызывает исключение. В нижеследующих описаниях функций пусть они i находятся в диапазоне [0, sizeof...(Types)) по порядку, Ti должны быть ith типом в Typesи Ui быть ith типом в названном пакете параметров шаблона UTypes, где индексирование начинается с нуля.
tuple& operator=(const tuple& u);
Remarks: Этот оператор должен быть определен как удаленный, если он не is_copy_assignable_v<Ti> предназначен true для всех i.
tuple& operator=(tuple&& u) noexcept(see below);
Remarks: Этот оператор должен быть определен как удаленный, если он не is_move_assignable_v<Ti> предназначен true для всех i.
Remarks: Выражение внутри noexcept эквивалентно логическому и следующих выражений:
is_nothrow_move_assignable_v<Ti>
где Ti это ith тип в Types.
template <class... UTypes> tuple& operator=(const tuple<UTypes...>& u);
Remarks: Этот оператор не должен участвовать в разрешении перегрузки , если sizeof...(Types) == sizeof...(UTypes) и is_assignable_v<Ti&, const Ui&> не true для всех i.
template <class... UTypes> tuple& operator=(tuple<UTypes...>&& u);
Remarks: Этот оператор не должен участвовать в разрешении перегрузки, кроме как is_assignable_v<Ti&, Ui&&> == true для всех i и sizeof...(Types) == sizeof...(UTypes).
template <class U1, class U2> tuple& operator=(const pair<U1, U2>& u);
Remarks: Этот оператор не должен участвовать в разрешении перегрузки , если sizeof...(Types) == 2 и is_assignable_v<T0&, const U1&> не true для первого типа T0 в Types и is_assignable_v<T1&, const U2&> является true для второго типа T1 в Types.
template <class U1, class U2> tuple& operator=(pair<U1, U2>&& u);
Effects: Присваивается std::forward<U1>(u.first) первому элементу *this и
std::forward<U2>(u.second) второму элементу *this.
Remarks: Этот оператор не должен участвовать в разрешении перегрузки , если sizeof...(Types) == 2 и is_assignable_v<T0&, U1&&> не true для первого типа T0 в Types и is_assignable_v<T1&, U2&&> является true для второго типа T1 в Types.
void swap(tuple& rhs) noexcept(see below);
Requires: Каждый элемент в *this должен быть заменен на ([swappable.requirements]) соответствующий элемент в rhs.
Remarks: Выражение внутри noexcept эквивалентно логическому и следующих выражений:
is_nothrow_swappable_v<Ti>
где Ti это ith тип в Types.
В нижеследующих описаниях функций элементы пакета параметров XTypes обозначаются по порядку с помощью Xi for i in [0, sizeof...(XTypes)) , где индексирование начинается с нуля.
template<class... TTypes>
constexpr tuple<VTypes...> make_tuple(TTypes&&... t);
Пакет VTypes определяется следующим образом. Пусть Ui будет decay_t<Ti> для каждого Ti в TTypes. Если Ui - это специализация reference_wrapper, то Vi в VTypes - Ui::type&, иначе Vi - Ui.
[ Example:
int i; float j; make_tuple(1, ref(i), cref(j))
создает кортеж типа tuple<int, int&, const float&>. ] — end example
template<class... TTypes>
constexpr tuple<TTypes&&...> forward_as_tuple(TTypes&&... t) noexcept;
Effects: Создает кортеж ссылок на аргументы, t пригодный для пересылки в качестве аргументов функции. Поскольку результат может содержать ссылки на временные переменные, программа должна гарантировать, что возвращаемое значение этой функции не переживет ни один из ее аргументов (например, программа обычно не должна сохранять результат в именованной переменной).
template<class... TTypes>
constexpr tuple<TTypes&...> tie(TTypes&... t) noexcept;
Returns: tuple<TTypes&...>(t...). Когда аргумент в t is ignore, присвоение любого значения соответствующему элементу кортежа не имеет никакого эффекта.
[ Example: tie функции позволяют создавать кортежи, которые распаковывают кортежи в переменные. ignore можно использовать для элементов, которые не нужны:
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);
В следующих параграфах пусть Ti будет ith типом Tuples, Ui быть remove_reference_t<Ti>и tpi будет ith параметром в пакете параметров функции tpls, где все индексации отсчитываются от нуля.
Requires: Для всех i, Ui должен быть тип cvi tuple<Argsi...>, где cvi это (возможно , пустой) и является параметром пакет , представляющий типы элементов в . Позвольте быть типом в . Для всех должны быть выполнены следующие требования: ith cv-qualifier-seq Argsi Ui Aik kth ArgsiAik
Если Ti выводится как ссылочный тип lvalue, то в is_constructible_v<Aik, cviAik&> == trueпротивном случае
is_constructible_v<Aik, cviAik&&> == true.
Remarks: Типы в CTypes должны быть равны упорядоченной последовательности расширенных типов Args0..., Args1..., …, Argsn−1..., где n равно sizeof...(Tuples). Позвольте ei... быть ith упорядоченной последовательностью элементов кортежа результирующего tuple объекта, соответствующей последовательности типов Argsi.
Returns: tuple Объект , построенный по инициализации kith элемента типа eik в ei... с
get<ki>(std::forward<Ti>(tpi))
для каждой действующей ki и каждой группы ei по порядку.
template <class F, class Tuple>
constexpr decltype(auto) apply(F&& f, Tuple&& t);
Effects: Учитывая функцию только для экспозиции:
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))...); }
Эквивалентен:
return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), make_index_sequence<tuple_size_v<decay_t<Tuple>>>{});
template <class T, class Tuple>
constexpr T make_from_tuple(Tuple&& t);
Effects: Учитывая функцию только для экспозиции:
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))...);
}
Эквивалентен:
return make_from_tuple_impl<T>(forward<Tuple>(t), make_index_sequence<tuple_size_v<decay_t<Tuple>>>{});
[ Note: Тип T должен быть указан как явный параметр шаблона, так как он не может быть выведен из списка аргументов. ] — end note
template <class T> struct tuple_size;
Remarks: Все специализации tuple_size должны соответствовать UnaryTypeTrait требованиям с базовой характеристикой integral_constant<size_t, N> для некоторых 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). Программа некорректно сформирована, если I выходит за пределы допустимого диапазона.
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>;
Обозначим TS через tuple_size<T> - cvнеквалифицированный тип T. Если выражение TS::value правильно сформировано, когда рассматривается как неоцененный операнд, то каждый из трех шаблонов должен соответствовать UnaryTypeTrait требованиям с базовой характеристикой
integral_constant<size_t, TS::value>
В противном случае у них не будет ни одного члена value.
Проверка доступа выполняется, как если бы в контексте, не связанном с TS и T. Учитывается только действительность непосредственного контекста выражения. [ Note: Компиляция выражения может привести к побочным эффектам, таким как создание экземпляров специализаций шаблонов классов и специализаций шаблонов функций, создание неявно определенных функций и т. Д. Такие побочные эффекты не относятся к «непосредственному контексту» и могут привести к неправильному формированию программы. ] — end note
Помимо того <tuple> , что эти три шаблона доступны через включение заголовка, они доступны либо при включении заголовков, <array> либо <utility> .
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>;
Обозначим TE через tuple_element_t<I, T> - cvнеквалифицированный тип T. Затем каждый из трех шаблонов должен соответствовать TransformationTrait требованиям с помощью typedef члена, type который называет следующий тип:
для первой специализации add_const_t<TE>,
для второй специализации add_volatile_t<TE>, и
для третьей специализации add_cv_t<TE>.
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). Программа некорректно сформирована, если I выходит за пределы допустимого диапазона.
[ Note A: Если T in Types - некоторый ссылочный тип X&, возвращаемый тип - X&нет X&&. Однако, если тип элемента не является ссылочным типом T, возвращается тип T&&. ] — end note
[ Note B: Констанс неглубокий. Если T in Types - некоторый ссылочный тип X&, возвращаемый тип - X¬ const X&. Однако, если тип элемента не является ссылочным типом T, возвращается тип const T&. Это согласуется с тем, как константность определяется для работы с переменными-членами ссылочного типа. ] — 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: Тип T встречается ровно один раз 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 ]
template<class... TTypes, class... UTypes>
constexpr bool operator==(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
Requires: Для всех i, где 0 <= i и i < sizeof...(TTypes), get<i>(t) == get<i>(u) является действительным выражением , возвращающая тип , который конвертируется в bool. sizeof...(TTypes) == sizeof...(UTypes).
Returns: true если get<i>(t) == get<i>(u) для всех i, иначе false. Для любых двух кортежей нулевой длины e и f, e == f возвратов true.
Effects: Элементарные сравнения выполняются в порядке от нулевого индекса вверх. Никакие сравнения или обращения к элементам не выполняются после первого сравнения на равенство, которое оценивается как false.
template<class... TTypes, class... UTypes>
constexpr bool operator<(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
Requires: Для всех i, where 0 <= i и i < sizeof...(TTypes)оба get<i>(t) < get<i>(u) и get<i>(u) < get<i>(t) являются допустимыми выражениями, возвращающими типы, в которые можно преобразовать bool. sizeof...(TTypes) == sizeof...(UTypes).
Returns: Результат лексикографического сравнения между t и u. Результат определяется как:, (bool)(get<0>(t) < get<0>(u)) || (!(bool)(get<0>(u) < get<0>(t)) && ttail < utail)где rtail для некоторого кортежа r это кортеж, содержащий все элементы, кроме первого r. Для любых двух кортежей нулевой длины e и f, e < f возвратов false.
template<class... TTypes, class... UTypes>
constexpr bool operator!=(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
template<class... TTypes, class... UTypes>
constexpr bool operator>(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
template<class... TTypes, class... UTypes>
constexpr bool operator<=(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
template<class... TTypes, class... UTypes>
constexpr bool operator>=(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
[ Note: Приведенные выше определения функций сравнения не требуют ttail (или utail) создания. Это может быть даже невозможно, поскольку t и u не обязательно должны быть копируемыми. Кроме того, все функции сравнения замкнуты; они не осуществляют доступ к элементам сверх того, что требуется для определения результата сравнения. ] — end note
template <class... Types, class Alloc>
struct uses_allocator<tuple<Types...>, Alloc> : true_type { };
template <class... Types>
void swap(tuple<Types...>& x, tuple<Types...>& y) noexcept(see below);
Remarks: Эта функция не должна участвовать в разрешении перегрузки, кроме случаев, когда она is_swappable_v<Ti> предназначена true для всех i, где 0≤i<sizeof...(Types). Выражение внутри noexcept эквивалентно:
noexcept(x.swap(y))