Кортежи std::tuple в C++
Этот пост долгое время лежал в черновиках и, по всей видимости, это был вольный перевод одной из статей(ссылки ниже) о классе 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<> можно создавать кортежи :
Можно также задавать значения у элементов кортежа путем их последовательного перечисления в конструкторе :
Если элемент кортежа является ссылочным типом, то его значение должно быть определено :
Функция make_tuple упрощает создание кортежей, причем специализацию шаблона возвращаемого кортежа в ряде случаев можно указать один раз :
Если функция make_tuple принимает ссылочное значение, то оно приводится к обычному нессылочному значению
При помощи использования функций cref и ref в make_tuple можно добиться создания кортежа, в котором элементы являются ссылками :
Доступ к элементам кортежа
Получить n-й элемент кортежа t можно двумя способами
В C++ также допустимо изменение значений элементов кортежа, хотя в ряде языков это запрещено
Стоит не забывать, что тип изменяемого элемента должен быть изменяемым, т.е. не содержать модификатор const и иметь открытый оператор =.
Шаблонный класс std::tuple<> позволяет менять содержимое кортежа путем использования оператора равенства(operator =), но количество параметров в специализациях копируемых кортежей должно строго совпадать :
В примере выше произойдет конвертация int -> char и B* -> A(т.к. A является полиморфным). Если конвертация типа не возможна, произойдет ошибка на этапе компиляции.
Старый кортеж можно скопировать в новый кортеж путем использования конструктора копий :
Распаковка кортежа и получение значений некоторых элементов
Распаковка(unpacking) - одна из основных, часто используемых функциональных возможностей кортежей. Распаковка позваоляет получать из кортежа сразу несколько значений в отдельные переменные, с которыми дальше проще работать.
Для распаковки используется специальная шаблонная функция std::tie, которой передаются переменные, которым необходимо присвоить значения из кортежа.
Для примера, теперь мы можем написать функцию swap в одну строчку :
В принципе, в использовании подобной конструкции и заключается преимущество кортежей - посколько с правой стороны мог стоять вызов какой-то другой функции, возвращающей тип tuple. Нужно это или нет - другой вопрос, зависящий от конкретного кода, потому что всегда были и будут альтернативные подходы - изменяемые параметры функций вида pass by reference, а также какие-то внешние обертки для сохранения внутреннего состояния(структуры, классы, пространства имён).