Еще один вариант реализации — использовать в качестве параметра шаблон (см. п. п. 14.3.3 в [1]). Это дает нам возможность иметь разные реализации очередей в зависимости от контейнера, который передается классу-шаблону TQueue в качестве аргумента. Параметру-шаблону, как и любому другому виду параметров шаблонного класса, можно присвоить значение по умолчанию — пусть это будет наш класс TSimpleArray. Посмотрите, как это делается (листинг 11.8).
Листинг 11.8. Шаблонный параметр шаблона
template <typename Т, // тип элементов
std::size_t N = 1000, // количество
template <class, std::size_t> // параметр-шаблон
class Container = TSimpleArray // значение по умолчанию
> // конец списка
class TQueue
{ Container<T, N> t; // поле-контейнер
public: // методы
};
Параметр-шаблон объявлен последним:
template <class, std::size_t> class Container = TSimpleArray
1 Не запутались? Можно сказать по-другому: имена параметров параметра-шаблона.
Так же как обычный шаблон, параметр начинается с ключевого слова template, за которым следует список его параметров. Однако в списке параметров параметра-шаблона не задаются имена параметров1 — они просто не используются.
Можно провести аналогию с неиспользуемым параметром при реализации постфиксной операции инкремента operation++(int).
Объявление такого контейнера в программе может выглядеть так:
TQueue<double> ql; // размер и контейнер по умолчанию
TQueue<double, 5000> q2; // контейнер по умолчанию
TQueue<double, 500, TSimpleArray> q3; // все задано явно
Обратите внимание, что в третьем варианте задается только имя контейнера без аргументов. Вместо нашего массива подойдет любой контейнер, имеющий два аналогичных параметра: тип элементов и беззнаковое целое. Вспомним «умный» массив ТАггау (см. листинг 6.10) и преобразуем его в шаблон. Интерфейс нового шаблонного класса практически не отличается от интерфейса TSimpleArray (листинг 11.9).
Листинг 11.9. Шаблонный класс ТАггау
template <typename Т = double, std::size_t n = 100>
class TArray
{ public:
// определение типов
typedef std::size_t size_type;
typedef std::size_t index_type;
typedef T value_type;
typedef T& reference;
typedef T* iterator;
typedef T* pointer;
TArray(size_type size, value_type k=0.0);
TArray(const TArray &a);
TArray(const TArray &a, index_type begin, size_type k); TArray(const iterator begin, const iterator end); ~TArray();
reference operator[](index_type index);
const reference operator[](index_type index) const;
TArray& operator=(const TArray &a);
TArray& assign(const TArray &a, index_type I, index_type r); TArray& assign(const iterator begin, const iterator end); size_type sizeQconst { return size_array; }; iterator find(const value_type &a); friend ostream& operator <<(ostream& to, const TArray &a); friend istream& operator >>(istream& to, TArray &a); private:
size_type size_array; pointer data;
};
Обратите внимание, как мало нам пришлось изменить для преобразования класса ТАггау в шаблон: фактически добавился только заголовок шаблона и в определении типов слово double заменено параметром шаблона Т. При правильном подходе возможные изменения инкапсулируются в небольшой области текста и минимизируются.
Параметры шаблона сделаны точно такими, как в шаблоне TSimpleArray. Поэтому этот класс можно использовать в качестве аргумента при объявлении объекта-очереди:
TQueue<double, 500, TArray> Q4; // все задано явно
Отметим некоторые особенности объявления параметра-шаблона. Во-первых, количество, порядок и типы параметров параметра-шаблона должны в точности соответствовать параметрам подставляемого аргумента-шаблона (в данном случае — TSimpleArray). Нельзя, например, переставить их местами:
template <std::size_t, class> class Container = TSimpleArray
Точно такие же параметры имеет и второй шаблонный класс — ТАггау.
Во-вторых, в списке параметров параметра-шаблона разрешается вместо слова class использовать слово typename, например:
template <typename, std::size_t> class Container = TSimpleArray
Однако перед именем самого параметра-шаблона можно писать только слово class:
class Contaner
Не разрешается писать ни слово typename, ни слово struct, ни слово union.
В-третьих, некоторые проблемы доставляют параметры по умолчанию. Наш контейнер TSimpleArray такой параметр имеет — это количество элементов массива N. В шаблоне TQueue задан собственный параметр N и ему присвоено значение по умолчанию 1000. Однако очередь, вообще говоря, — структура данных потенциально бесконечная, поэтому количество элементов внутреннего контейнера должно быть его «внутренним» делом; предполагается, что контейнер самостоятельно управляется со своей памятью. Но задавать значения по умолчанию в шаблоне-параметре нельзя — его имена компилятором не рассматриваются в качестве аргументов! Например:
template <typename Т,
template <class, std::size__t N = 100> class Container=TSimpleArray
>
Этот заголовок шаблона TQueue в Visual C++.NET 2003 при объявлении поля-контейнера Container <Т, N> t; вызывает ошибку компиляции о неопределенном имени N:
error С2065: 'N' : undeclared identifier
Поэтому нам пришлось вынести этот параметр на верхний уровень. |