библиотека · утилитыlibrary · utilities
Гетерогенный набор значений фиксированного размера, известного на этапе компиляции. Обобщение std::pair на произвольное число элементов — построено на variadic templates.A heterogeneous, fixed-size collection of values whose size is known at compile time. A generalization of std::pair to an arbitrary number of elements — built on variadic templates.
std::tuple<Types...> — контейнер фиксированного размера, хранящий по одному значению каждого из перечисленных типов. Доступ к элементу — по индексу через std::get<N>(t) (индекс — компайл-тайм константа, не рантайм-переменная), а не по имени поля, как в struct.std::tuple<Types...> is a fixed-size container holding one value of each of the listed types. Element access is by index via std::get<N>(t) (the index is a compile-time constant, not a runtime variable), rather than by field name as in a struct.
std::pair<std::string, int> p = {"age", 30};
std::cout << p.first << p.second;
std::tuple<std::string, int, double> t =
{"age", 30, 1.75};
std::cout << std::get<0>(t) << std::get<1>(t);
std::tuple закрывает разрыв между std::pair (ограничен двумя элементами) и полноценным struct (требует объявления):std::tuple fills the gap between std::pair (limited to two elements) and a full struct (requires a declaration):
Функции, которым нужно вернуть три и более разнородных значения, до tuple были вынуждены либо объявлять именованный struct для каждого случая, либо использовать выходные параметры по ссылке — оба варианта многословнее одной строки с tuple.Functions that needed to return three or more heterogeneous values, before tuple, were forced to either declare a named struct for each case or use reference out-parameters — both options more verbose than a single line with tuple.
Временная группировка нескольких значений (например, для сортировки по нескольким ключам через сравнение tuple целиком, которое сравнивает лексикографически) не требует объявления отдельного struct с operator<.Temporarily grouping several values (for example, to sort by multiple keys via a whole-tuple comparison, which compares lexicographically) doesn't require declaring a separate struct with operator<.
Реализация tuple с произвольным числом элементов разных типов до variadic templates требовала генерации кода через макросы препроцессора для каждого N — вариативные шаблоны, добавленные тем же стандартом, сделали tuple тривиальной с точки зрения реализации библиотечной фичей.Implementing a tuple with an arbitrary number of elements of different types, before variadic templates, required generating code via preprocessor macros for each N — variadic templates, added in the same standard, turned tuple into a library feature that's trivial to implement.
std::tuple реализован через рекурсивное наследование (или композицию) от специализаций с уменьшающимся списком типов — каждый уровень хранит один элемент пакета и наследуется от tuple с оставшимися. std::get<N> — не индексация массива в рантайме, а разрешение перегрузки/специализации шаблона на этапе компиляции, которое напрямую обращается к нужному полю без цикла.std::tuple is implemented via recursive inheritance (or composition) from specializations with a shrinking type list — each level stores one element of the pack and inherits from the tuple holding the rest. std::get<N> isn't a runtime array index — it's overload/specialization resolution at compile time that reaches the needed field directly, with no loop.
#include <tuple>
std::tuple<std::string, int, double> person("Alice", 30, 1.75);
std::string name = std::get<0>(person);
int age = std::get<1>(person);
// std::tie — распаковка в существующие переменные (structured bindings — C++17):// std::tie — unpacking into existing variables (structured bindings — C++17):
std::string n; int a; double h;
std::tie(n, a, h) = person;
// std::make_tuple выводит типы автоматически:// std::make_tuple deduces the types automatically:
auto t = std::make_tuple(1, "two", 3.0); // tuple<int, const char*, double>
// упрощённая идея внутреннего устройства (концептуально):// a simplified idea of the internal layout (conceptually):
template <class Head, class... Tail>
struct TupleImpl : TupleImpl<Tail...> { // рекурсивное наследование// recursive inheritance
Head value;
};
template <class Head>
struct TupleImpl<Head> { Head value; }; // база рекурсии — один элемент// recursion base case — a single element
std::get<i>(t), где i — обычная переменная времени выполнения, не компилируется — индекс обязан быть известен на этапе компиляции, потому что тип возвращаемого значения зависит от него. Перебор элементов tuple в цикле по рантайм-индексу невозможен напрямую, нужна метапрограммная развёртка через рекурсию по индексам.std::get<i>(t), where i is an ordinary runtime variable, doesn't compile — the index must be known at compile time because the return type depends on it. Iterating over tuple elements in a loop with a runtime index isn't possible directly; it needs a metaprogramming unroll via recursion over indices.
std::get<2>(t) ничего не говорит о семантике третьего элемента — если поля имеют устойчивый смысл (не временная группировка), обычный struct с именованными полями читается лучше и безопаснее при рефакторинге порядка полей.std::get<2>(t) says nothing about the semantics of the third element — if the fields carry a stable meaning (not a temporary grouping), an ordinary struct with named fields reads better and is safer when refactoring field order.
std::get<int>(t) (доступ по типу вместо индекса — работает и в C++11, хоть чаще ассоциируется с более новым кодом) требует, чтобы тип встречался в tuple ровно один раз — при повторяющихся типах (tuple<int, int>) такой вызов не компилируется из-за неоднозначности, нужен доступ по индексу.std::get<int>(t) (access by type instead of index — works in C++11 too, though more often associated with newer code) requires the type to appear in the tuple exactly once — with repeated types (tuple<int, int>) such a call fails to compile due to ambiguity, and index-based access is required instead.