- •Цель работы
- •2 Техническое обеспечение
- •3 Программное обеспечение
- •4 Постановка задачи
- •Общие сведения
- •5.1 Шаблоны классов
- •5.1.1 Пример класса-шаблона
- •5.1.2 Параметры шаблона, задаваемые по умолчанию
- •5.1.3 Специализация шаблона класса
- •5.1.4 Статические элементы в шаблонах
- •5.1.5 Шаблоны классов с шаблонами
- •5. 2 Шаблоны функций
- •5.3 Обобщенные алгоритмы и функторы
- •Варианты заданий
- •7 Указания к выполнению лабораторной работы
- •9 Контрольные вопросы
5.1.2 Параметры шаблона, задаваемые по умолчанию
Параметрам класса-шаблона можно присваивать значения по умолчанию, в том числе и для параметров-типов. Для целочисленных параметров допускается задавать в качестве значения константное выражение, которое компилятор способен вычислить на этапе трансляции. Для параметров-типов можно указывать либо встроенный тип, либо любое видимое в точке определения шаблона имя тина.
Например, для шаблона «умного» массива Array (листинг 11.1) можно присвоить по умолчанию тип double и ограничить массив десятью элементами. Заголовок шаблона пишется привычным способом
template <typename Т = double, std::size_t N = 10>
В реализации методов ничего изменять не требуется. Тогда допускаются объявления массивов такого вида:
Array <int, 20> t; // полное объявление
Array <date> d; // количество по умолчанию
Аггау <> р; // тип и количество по умолчанию
Пустые угловые скобки указывать обязательно, иначе возникает ошибка времени компиляции, поскольку компилятор будет искать обычный класс Array. Во всех объявлениях инициализация выполняется неявно. Класс date должен иметь конструктор без аргументов или конструктор инициализации с аргументом по умолчанию.
Как и для функций с аргументами по умолчанию, присваивать значения нужно правым параметрам. Можно написать заголовок шаблона Array так:
template <typename Т, std::size_t N = 100>
и не разрешается – таким способом:
template <typename Т = double, std::size_t N>
Как и для функций с аргументами по умолчанию, при определении объекта нельзя пропускать левые параметры, например:
Array<10> t; // ожидается ошибка трансляции!
Подобное объявление приведет к ошибке трансляции компилятор не обнаружит определения шаблона с одним целочисленным параметром.
5.1.3 Специализация шаблона класса
При программировании шаблонов классов часто бывает нужно наряду с общим шаблоном иметь специализированные версии. Для этого в С++ имеется механизм специализации. Специализация заключается в том, что на основе исходного первичного шаблона создается его специализированная версия, в которой на место параметров подставлены аргументы (и по-другому реализованы некоторые методы в соответствии с аргументами). Специализация это не присвоение параметров по умолчанию. Шаблон с параметрами по умолчанию является первичным шаблоном, который тоже можно специализировать. Специализация шаблона называется полной, если конкретизированы все аргументы. Если задана только часть аргументов, такая специализация называется частичной.
При определении шаблона и его специализированных версий должен соблюдаться порядок следования: сначала должен быть определен первичный шаблон, и только после него разрешается определять специализированные версии.
tempiate<class Т>
// первичный шаблон
class Class
{ // определения полей и методов класса
};
template < >
// полная специализация
class Class <void *>
{ // определения полей и методов специализированной версии класса
};
Аргументы шаблона, подлежащие специализации, указаны в угловых скобках после имени класса. В данном случае шаблон Class специализирован для бестиповых указателей. Объект такого класса нужно объявлять как объект класса-шаблона с аргументом void *, например:
Class <void *> ddd;
При частичной специализации конкретизируется только часть параметров первичного шаблона. Для указателей возможна частичная специализация, даже если параметр шаблона всего один, например:
template <class Т>
// частичная специализация
class Class<T*>
{ // определения полей и методов специализированной версии класса
};
Обозначение <Т*> после имени подразумевает, что эта специализация должна использоваться всегда, когда аргументом шаблона является указатель любого типа, отличного от void *, для которого «реализована более специализированная полная специализация». Определения объектов выглядят так:
Class <date*> m1; // <T*> - это <date*> -> T = date
Class <int*> m2; // <T*> - это <int*> -> T = int
Class <double**> m3; // <T*> - это <double**> -> T = double*
Чтобы специализировать шаблон, не обязательно иметь полное определение первичного шаблона достаточно объявления, например:
template <typename T> class Class: // объявление первичного шаблона
template <typename T> class Class <T*> // частичная специализация
{ // определения полей и методов специализированной версии класса
};
template <> class Class <void *> // полная специализация
{ // определения полей и методов специализированной версии класса
};
Все будет корректно работать, если не пытаться инстанцировать первичный шаблон.
