Кортеж (std::tuple) появился в стандартной библиотеке начиная с С++11. Это неоднородный список элементов, типы которых задаются или выводятся на этапе компиляции. Кортеж "похож" на пару (std::pair), однако он может содержать произвольное (хотя и конечное) количество элементов. Интерфейс кортежа довольно простой: http://en.cppreference.com/w/cpp/utility/tuple

Создать кортеж можно так:

#include <tuple>

std::tuple<std::string, int, double> tuple_("hello", 42, 3.14);

Про кортеж говорят, что это коллекция гетерогенных значений (разнородных). Поэтому тип элемента (value_type), который определен в любом стандартном контейнере, не имеет смысла в кортеже, и просто отсутствует, ровно как и любой из типов итераторов. Кортеж не является обычным контейнерным классом, и не отвечает концепции контейнеров в С++ (http://en.cppreference.com/w/cpp/concept/Container). Поэтому простого способа обхода элементов кортежа нет. К нему не применимы какие-либо циклы:

for (auto& x : tuple_);  // Ошибка компиляции!

error: no viable 'begin' function available

"Пройтись" по элементам кортежа можно только точно зная количество его аргументов на этапе компиляции. Для этого нужно использовать функцию get:

std::get<0>(tuple_); // индекс элемента в качестве аргумента шаблона
std::get<1>(tuple_);
std::get<2>(tuple_);

При этом передача индекса во время выполнения программы невозможна:

int i = 0;
std::get<i>(tuple_);  // Ошибка компиляции!

Однако, задействовав шаблонное метапрограммирование, можно создать более удобный способ обхода кортежа на этапе компиляции.

Для начала стоит привести пример, в каких случаях нам может понадобиться кортеж, и обход его элементов.

В стандарте С++11 появились вариативные шаблоны (variadic templates), и теперь мы можем, например, писать функции, которые способны принимать любое количество параметров разных типов.

Внутри функции к переданным значениям аргументов нужно "как-то обращаться". И здесь нам поможет кортеж. Мы можем сконструировать подходящий кортеж, используя вспомогательную функцию std::make_tuple

Используя интерфейс кортежа, можно работать с данными, которые были переданы функции. Например, попробуем вывести в std::out все элементы кортежа (значения аргументов функции).

Но вот проблема: в зависимости от того, сколько параметров будет передано функции, кортеж будет содержать разное количество элементов. И не смотря на то, что мы можем узнать количество элементов кортежа, с помощью вспомогательной функции std::tuple_size, написать обобщенный код все равно не удастся. По крайней мере, без "шаблонной магии", к которой далее мы и прибегнем.

Чтобы обработать каждый элемент кортежа, было бы удобно иметь функцию вида for_each, которая принимала бы сам кортеж и функцию-обработчик:

Чтобы создать такой for_each, нам потребуется придумать способ обхода элементов в кортеже. Кортежи впервые появились в библиотеке boost, и там, для обхода, применялась рекурсия. Тот же способ применим и мы:

Пример кода хорошо задокументирован, и, надеюсь, понятен. Здесь нет сложных конструкций и он достаточно мал. Но это - всё что нужно, чтобы реализовать for_each для кортежа! А теперь пример использования:

Всё просто! Стоит обратить внимание только на callback. Мы не можем определить его так:

Потому что, тогда бы потребовалось точно знать тип T, в момент передачи функции-обработчика. А этого мы не знаем в общем случае. Но шаблонные функции-члены не обязаны быть явно инстанцированы в момент создания экземпляра класса, а значит, мы можем применять их в качестве обработчиков для for_each.

Эта короткая статья из серии "рецепты для C++" мне показалась будет полезна на практике, поэтому, без всяких усложнений и углублений, постарался донести решение конкретной задачи.

Thank you for your attention!