Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Итог_Пособие C++.doc
Скачиваний:
8
Добавлен:
01.07.2025
Размер:
2 Мб
Скачать
☆

5.4 Шаблонные параметры шаблонов

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

std::vector<int> x = {1, 2, 3, 4, 5};

std::cout << x; // ошибка

Задача заключается в том, чтобы реализовать такую функциональность. Можно просто перегрузить операцию << для класса std::vector:

// Пример 5.8 - перегрузка операции << для std::vector<T>

template<class T>

std::ostream& operator<< (std::ostream& out, const std::vector<T> &t) {

for (auto &x: t)

// допускаем лишний пробел в конце вывода для простоты

out << x << " ";

return out;

}

Теперь операция << работает для векторов. Однако, в стандартной библиотеке есть и другие классы-контейнеры – например, список (std::list) и дек (std::deque). Возникает логичный вопрос – можно ли написать универсальную функцию, применимую к разным видам контейнеров? На первый взгляд может показаться, что функция может выглядеть так:

// данный код не скомпилируется!

template<class Container, class T>

std::ostream& operator<< (std::ostream& out, const Container<T> &t) {

for (auto &x: t)

out << x << " ";

return out;

}

Однако, данный код вызовет ошибку компиляции. Проблема в том, что в нашей функции мы используем выражение Container<T>, где и T, и Container – шаблонные параметры. Язык C++ требует, чтобы мы явно сказали о том, что шаблонный параметр Container сам является шаблонным классом, т.е. Container должен быть описан как шаблонный параметр шаблона (иногда говорят "шаблонный шаблонный параметр" – "template template parameter"). Это записывается следующим образом:

template< template<typename> class Container, class T >

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

std::vector<int> x; // используется стандартный аллокатор по умолчанию

std::vector<int, std::allocator<int> > x; // явным образом указали

//стандартный аллокатор

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

// Пример 5.9 - перегрузка операции << для разных контейнеров

template<template<typename, typename> class Container,

class T, class Alloc>

std::ostream& operator<< (std::ostream& out,

const Container<T, Alloc> &t) {

for (auto &x: t)

out << x << " ";

return out;

}

Приведём примеры использования перегруженной операции << для разных типов контейнеров:

std::vector<int> x = {1, 2, 3, 4, 5};

std::cout << x << std::endl;

std::list<double> y = {1.3, 4.5, 7.3};

std::cout << y << std::endl;

std::deque<std::string> z = {"ab", "cde"};

std::cout << z << std::endl;