Шрифт:
Интервал:
Закладка:
template <typename T1, typename T2>
static auto zip(const T1 &a, const T2 &b)
{
10. Мы подобрались к самым сложным строкам данного примера. Создадим объект функции z, принимающий произвольное количество аргументов. Затем он возвращает другой объект функции, который захватывает все эти аргументы в набор параметров xs, а также принимает произвольное количество аргументов. Забудем об этом на минуту. С помощью упомянутого внутреннего объекта функции можно получить доступ к обоим спискам аргументов в виде наборов параметров xs и ys. А теперь взглянем, что именно происходит с этими наборами параметров. Вызов make_tuple(xs,ys). группирует наборы параметров поэлементно. Т.е. при наличии наборов xs = 1,2,3 и ys = 'a','b','c' он вернет новый набор параметров (1,'a'), (2,'b'), (3,'c'). Данный набор представляет собой разделенный запятыми список, состоящий из трех кортежей. Чтобы объединить их в один кортеж, используем функцию std::tuple_cat, которая принимает произвольное количество кортежей и упаковывает их в один. Таким образом получим кортеж (1,'a',2,'b',3,'c').
auto z ([](auto ...xs) {
return [xs...](auto ...ys) {
return tuple_cat(make_tuple(xs, ys) );
};
});
11. Последний шаг состоит в том, чтобы распаковать все значения из входных кортежей a и b и поместить их в z. Вызов apply(z,a) помещает все значения из a в набор параметров xs, а вызов apply(...,b) помещает все значения b в набор параметров ys. Полученный кортеж представляет собой остальные сгруппированные кортежи, которые мы и возвращаем вызывающей стороне:
return apply(apply(z, a), b);
}
12. Мы написали довольно много строк кода для вспомогательных функций. Теперь воспользуемся ими. Сначала создадим произвольные кортежи. student содержит идентификатор, имя и средний балл студента. student_desc включает строки, которые описывают значение этих полей, в форме, доступной человеку. std::make_tuple — приятный помощник, поскольку определяет тип всех аргументов и создает подходящий тип кортежа:
int main()
{
auto student_desc (make_tuple("ID", "Name", "GPA"));
auto student (make_tuple(123456, "John Doe", 3.7));
13. Выведем на экран все, чем располагаем. Сделать это очень легко, поскольку мы только что реализовали соответствующую перегруженную версию оператора <<.
cout << student_desc << 'n'
<< student << 'n';
14. Кроме того, можем сгруппировать оба кортежа динамически с помощью std::tuple_cat и вывести их на экран следующим образом:
cout << tuple_cat(student_desc, student) << 'n';
15. Мы можем создать и новый сгруппированный кортеж, задействуя нашу функцию zip, и вывести его на экран:
auto zipped (zip(student_desc, student));
cout << zipped << 'n';
16. Не будем забывать и о функции sum_min_max_avg. Создадим список инициализации, который содержит некие числа, и передадим его в эту функцию. Чтобы слегка усложнить задачу, создадим еще один кортеж такого же размера, содержащий строки с описанием. Группируя эти кортежи, получим «аккуратные», чередующиеся выходные данные, которые увидим при запуске программы:
auto numbers = {0.0, 1.0, 2.0, 3.0, 4.0};
cout << zip(
make_tuple("Sum", "Minimum", "Maximum", "Average"),
sum_min_max_avg(numbers))
<< 'n';
}
17. Компиляция и запуск программы дадут следующий результат. Первые две строки представляют отдельные кортежи student и student_desc. Третья строка — это комбинация кортежей, которую мы получили с помощью вызова tuple_cat. Четвертая содержит сгруппированный кортеж для студента. В последней строке видим сумму, а также минимальное, максимальное и среднее значения для созданного нами списка чисел. Благодаря группировке очень легко понять смысл каждого значения.
$ ./tuple
(ID, Name, GPA)
(123456, John Doe, 3.7)
(ID, Name, GPA, 123456, John Doe, 3.7)
(ID, 123456, Name, John Doe, GPA, 3.7)
(Sum, 10, Minimum, 0, Maximum, 4, Average, 2)
Как это работает
Отдельные фрагменты кода, представленные в этом разделе, довольно сложны. Мы написали реализацию operator<< для кортежей, которая выглядит очень сложной, но зато поддерживает все виды кортежей, содержащих выводимые типы. Далее мы реализовали функцию sum_min_max_avg, возвращающую кортеж. Еще одним сложным моментом является функция zip.
Самой простой частью была функция sum_min_max_avg. Ее идея заключается в следующем: при реализации функции, которая возвращает экземпляр типа tuple<Foo, Bar, Baz> f(), можно просто написать return {foo_instance, bar_ instance, baz_instance}; внутри этой функции, чтобы создать такой кортеж. Если вам трудно понять использованные нами алгоритмы STL, то, вероятно, следует обратиться к главе 5, где мы уже рассмотрели их подробнее.
Остальной код настолько сложен, что мы посвятим конкретным вспомогательным функциям отдельные подразделы.
operator<< для кортежей
До работы с operator<< для потоков вывода мы реализовали функцию print_args. Из-за своей природы он принимает произвольное количество аргументов разных типов до тех пор, пока первым из них является экземпляр типа ostream:
template <typename T, typename Ts>
void print_args(ostream &os, const T &v, const Ts &. vs)
{
os << v;
(void)initializer_list<int>{((os << ", " << vs), 0)...};
}
Эта функция выводит на экран первый элемент v, а затем все элементы из набора параметров vs. Мы выводим на экран первый элемент отдельно, поскольку хотим, чтобы все элементы были разделены запятыми, но не хотим, чтобы строка начиналась или заканчивалась запятой (например, "1,2,3, " или ",1,2,3"). Мы узнали о приеме с разворачиванием initializer_list из примера «Вызов нескольких функций с одними и теми же входными данными» главы 4. Подготовив эту функцию, мы получили все необходимое для вывода кортежей на экран. Наша реализация оператора << выглядит следующим образом:
template <typename ... Ts>
ostream& operator<<(ostream &os, const tuple<Ts...> &t)
{
auto capt_tup ([&os](const auto &...xs) {
print_args(os, xs...);
});
os << "(";
apply(capt_tup, t);
return os << ")";
}
Первое, что мы делаем, — это определяем объект функции capt_tup. Вызов capt_tup(foo, bar, whatever) приводит к вызову print_args(os, foo, bar, whatever). Данный объект функции делает только одно: добавляет к списку аргументов объект потока вывода os.
После этого