17.1.1. Определение и инициализация кортежей

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

tuple<size_t, size_t, size_t> threeD; // все три члена установлены в 0

tuple<string, vector<double>, int, list<int>>

 someVal("constants", {3.14, 2.718}, 42, {0,1,2,3,4,5});

При создании объекта кортежа можно использовать либо стандартный конструктор кортежа, инициализирующий каждый член по умолчанию (см. раздел 3.3.1), либо предоставить инициализатор для каждого члена, как при инициализации кортежа someVal. Этот конструктор кортежа является явным (см. раздел 7.5.4), поэтому следует использовать прямой синтаксис инициализации:

tuple<size_t, size_t, size_t> threeD = {1,2,3}; // ошибка

tuple<size_t, size_t, size_t> threeD{1,2,3};    // ok

В качестве альтернативы, подобно функции make_pair() (см. раздел 11.2.3), можно использовать библиотечную функцию make_tuple(), создающую объект кортежа:

// кортеж, представляющий транзакцию приложения книжного магазина:

// ISBN, количество, цена книги

auto item = make_tuple("0-999-78345-X", 3, 20.00);

Подобно функции make_pair(), функция make_tuple() использует типы, предоставляемые в качестве инициализаторов, для вывода типа кортежа. В данном случае кортеж item имеет тип tuple<const char*, int, double>.

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

В типе pair всегда есть два члена, что позволяет библиотеке присвоить им имена first (первый) и second (второй). Для типа tuple такое соглашение об именовании невозможно, поскольку у него нет ограничений на количество членов. В результате члены остаются безымянными. Вместо имен для обращения к членам кортежа используется библиотечный шаблон функции get. Чтобы использовать шаблон get, следует определить явный аргумент шаблона (см. раздел 16.2.2), задающий позицию члена, доступ к которому предстоит получить. Функция get() получает объект кортежа и возвращает ссылку на его заданный член:

auto book = get<0>(item);      // возвращает первый член item

auto cnt = get<1>(item);       // возвращает второй член item

auto price = get<2>(item)/cnt; // возвращает последний член item

get<2>(item) *= 0.8;           // применяет 20%-ную скидку

Значение в скобках должно быть целочисленным константным выражением (см. разделе 2.4.4). Как обычно, счет начинается с 0, а значит, первым членом будет get<0>.

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

typedef decltype(item) trans; // trans - тип кортежа item

// возвращает количество членов в объекте типа trans

size_t sz = tuple_size<trans>::value;             // возвращает 3

// cnt имеет тот же тип, что и второй член item

tuple_element<1, trans>::type cnt = get<1>(item); // cnt - это int

Для использования шаблонов tuple_size и tuple_element необходимо знать тип объекта кортежа. Как обычно, проще всего определить тип объекта при помощи спецификатора decltype (см. раздел 2.5.3). Здесь спецификатор decltype используется для определения псевдонима для типа кортежа item, который и используется при создании экземпляров обоих шаблонов.

Шаблон tuple_size обладает открытой статической переменной-членом value, содержащей количество членов в указанном кортеже. Шаблон tuple_element получает индекс, а также тип кортежа. Он обладает открытым типом-членом type, содержащим тип указанного члена кортежа заданного типа. Подобно функции get(), шаблон tuple_element ведет отсчет индексов начиная с нуля.

Операторы сравнения и равенства

Операторы сравнения и равенства кортежей ведут себя подобно соответствующим операторам контейнеров (см. раздел 9.2.7). Эти операторы выполняются для членов двух кортежей, слева и справа. Сравнить два кортежа можно только при совпадении количества их членов. Кроме того, чтобы использовать операторы равенства или неравенства, должно быть допустимо сравнение каждой пары членов при помощи оператора ==; а для использования операторов сравнения допустимым должно быть использование оператора <. Например:

tuple<string, string> duo("1", "2");

tuple<size_t, size_t> twoD(1, 2);

bool b = (duo == twoD); // ошибка: нельзя сравнить size_t и string

tuple<size_t, size_t, size_t> threeD(1, 2, 3);

b = (twoD < threeD);    // ошибка: разное количество членов

tuple<size_t, size_t> origin(0, 0);

b = (origin < twoD);    // ok: b — это true

Поскольку кортеж определяет операторы < и ==, последовательности кортежей можно передавать алгоритмам, а также использовать кортеж как тип ключа в упорядоченном контейнере.

Упражнения раздела 17.1.1

Упражнение 17.1. Определите кортеж, содержащий три члена типа int, и инициализируйте их значениями 10, 20 и 30.

Упражнение 17.2. Определите кортеж, содержащий строку, вектор строки и пару из строки и целого числа (типы string, vector<string> и pair<string, int>).

Упражнение 17.3. Перепишите программы TextQuery из раздела 12.3 так, чтобы использовать кортеж вместо класса QueryResult. Объясните, что на ваш взгляд лучше и почему.