Этот пост долгое время лежал в черновиках и, по всей видимости, это был вольный перевод одной из статей(ссылки ниже) о классе std::tuple<> C++. Идея перевода возникла спонтанно, в необходимости понимания сложности работы с этим классом. До C++0x неплохим, но все-таки усложняющим решением являлось использование более сложных структур в качестве хранилища для многих переменных(вдобавок, написание таких структур).
Текущее решение с кортежами и связкой tie/make_tuple выглядит более красиво и, для начала, с успехом позволит прототипировать код, в котором появляется необходимость в использовании кортежей.

Кортеж - коллекция элементов с фиксированным размером. Любая связанная пара, тройка, четверка и т.д элементов является кортежем. В качестве элементов кортежа могут выступать переменные произвольного типа.

Кортежи можно создавать, сравнивать, распаковывать. Фактически это всё что нужно для работы с кортежами. Но некоторые языки позволяют менять значения в элементах кортежей.

Во многих случаях удобно использование кортежи. Например, кортежи позволяют легко определять и работать с функциями, возвращающими одно или более значений.

Многие языки программирования, такие как Python, Haskell, Erlang и др. имеют встроенные кортежи. Однако, C++ не имеет встроенных кортежей.

Кортежи в C++

Первая реализация кортежей для C++ появилась в библиотеке boost в 2003 году.

Необходимость включения кортежей в основной стандарт подтверждается и тем, что они были включены в состав Technical Report 1 C++(TR1), реализация tr1 появилась в VS 2008 в составе Visual C++ 2008 Feature Pack Release в пространстве имен std::tr1, в VS 2010 кортежи уже включены по умолчанию и входят в стандартное пространство имен std. В стандарте C++0X кортежи входят в пространство имен std.

В C++ сохранились основные свойства, присущие кортежам - их можно создавать, сравнивать и распаковывать. Кроме того, значения C++ кортежей можно менять. Кортежи описываются шаблонным классом std::tuple<> в заголовочном файле , параметры специализации которого - перечисление всех типов переменных кортежа.

Создание кортежа

Путем инстанцирования шаблона std::tuple<> можно создавать кортежи :

std::tuple<int> t1;
std::tuple<int, std::string> t2;

Можно также задавать значения у элементов кортежа путем их последовательного перечисления в конструкторе :

std::tuple<int> t1(1);
std::tuple<int, std::string> t2(1, "hello");

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

std::tuple<double&> t(); // error
double d = 5;
std::tuple<double&> t(d); // ok

Функция make_tuple упрощает создание кортежей, причем специализацию шаблона возвращаемого кортежа в ряде случаев можно указать один раз :

std::tuple<int, int, double> add_multiply_divide(int a, int b) {
  return std::make_tuple(a+b, a*b, double(a)/double(b));
}

Если функция make_tuple принимает ссылочное значение, то оно приводится к обычному нессылочному значению

const A& a;
B& a;
std::make_tuple(a, b); // результат - tuple<A, B>

При помощи использования функций cref и ref в make_tuple можно добиться создания кортежа, в котором элементы являются ссылками :

const A& a;
B& a;
std::make_tuple(cref(a), ref(b)); // результат - tuple<const A&, B&>

Доступ к элементам кортежа

Получить n-й элемент кортежа t можно двумя способами

t.get<n>();
// или
get<n>(t);

В C++ также допустимо изменение значений элементов кортежа, хотя в ряде языков это запрещено

get<1>(t) = 3.14;  // ok
t.get<1>() = 3.14; // ok

Стоит не забывать, что тип изменяемого элемента должен быть изменяемым, т.е. не содержать модификатор const и иметь открытый оператор =.

Шаблонный класс std::tuple<> позволяет менять содержимое кортежа путем использования оператора равенства(operator =), но количество параметров в специализациях копируемых кортежей должно строго совпадать :

class A {};
class B : public A {};
struct C { C(); C(const B&); };
struct D { operator C() const; };
std::tuple<char, B*, B, D> t;
  ...
std::tuple<int, A*, C, C> a;
  ...
a = t;                       // ok

В примере выше произойдет конвертация int -> char и B* -> A(т.к. A является полиморфным). Если конвертация типа не возможна, произойдет ошибка на этапе компиляции.

Старый кортеж можно скопировать в новый кортеж путем использования конструктора копий :

std::tuple<char, int> t;
  ...
std::tuple<char, int> a(t); // ok

Распаковка кортежа и получение значений некоторых элементов

Распаковка(unpacking) - одна из основных, часто используемых функциональных возможностей кортежей. Распаковка позваоляет получать из кортежа сразу несколько значений в отдельные переменные, с которыми дальше проще работать.

Для распаковки используется специальная шаблонная функция std::tie, которой передаются переменные, которым необходимо присвоить значения из кортежа.

Для примера, теперь мы можем написать функцию swap в одну строчку :

int a = 10, b = 0;
std::tie(a, b) = std::make_tuple(b, a);

В принципе, в использовании подобной конструкции и заключается преимущество кортежей - посколько с правой стороны мог стоять вызов какой-то другой функции, возвращающей тип tuple. Нужно это или нет - другой вопрос, зависящий от конкретного кода, потому что всегда были и будут альтернативные подходы - изменяемые параметры функций вида pass by reference, а также какие-то внешние обертки для сохранения внутреннего состояния(структуры, классы, пространства имён).