Лекция 22. Шаблоны (часть 2) Красс Александр СПбГУ ИТМО, 2008
Темы Неполное инстанцирование Полная специализация Факториал на этапе компиляции typename
Неполное инстанцирование Рассмотрим шаблонную функцию возведения в степень: template T Power ( T v ) { T res = v; for ( int i=1; i<N; i++ ) res *= v; return res; } Функцию Power можно вызвать двумя способами: 1. int r = Power (1); 2. int r = Power (1); // Вот это и есть не полное инстанцирование. Компилятор самостоятельно выводит недостающие аргументы шаблона
Полная специализация Рассмотрим пример функции сравнения двух величин: template bool less ( T v1, T v2 ) { return v1 < v2; } Примеры использования: bool l1 = less(1,2); bool l2 = less(1.2,3.4); bool l4 = less(abcd,abcx); // Будет ли это работать правильно?
Полная специализация (2) Для последнего примера функция будет выглядеть так: bool less ( char* v1, char * v2 ) { return v1 < v2; // сравнение указателей, а не строк!!! } Итого: 1. Мы хотим иметь обобщенную форму less 2. Версию сравнения строк.
Полная специализация (3) Можно пойти двумя путями: 1.Перегрузка: bool less(const char *s1, const char *s2) { return strcmp(s1, s2) < 0; } 2. Явная специализация шаблона: template<> bool less ( const char* s1, const char* s2 ) { return strcmp(s1,s2)<0; } Теперь наш пример работает правильно Для функций перегрузка и полная явная специализация шаблона эквиваленты. Но для классов понятия перегрузки нет.
Полная специализация (4) Пример из STL: Обобщенная версия template class vector { … }; - динамический массив Специализация template<> class vector { …}; - массив флагов, каждый флаг это один бит. Реализовано в целях оптимизации по памяти.
Факториал на этапе компиляции Классический пример из области шаблонов: template unsigned long Fact ( void ) { if ( N<2 ) return 1; return N*Fact (); } Работать не будет!
unsigned long f5 = Fact (); template<> unsigned long Fact ( void ) { if ( 3<2 ) return 1; return 3*Fact (); } template<> unsigned long Fact ( void ) { if ( 2<2 ) return 1; return 2*Fact (); } template<> unsigned long Fact ( void ) { if ( 1<2 ) return 1; return 1*Fact (); } template<> unsigned long Fact ( void ) { if ( 0<2 ) return 1; return 0*Fact (); } Факториал на этапе компиляции (2) Получаем бесконечную рекурсию!
Факториал на этапе компиляции (3) А вот так работать будет: 1. Обобщенная версия template unsigned long Fact ( void ) { return N*Fact (); } 2. Явная специализация для частных случаев: template<> unsigned long Fact ( void ) { return 1; } template<> unsigned long Fact ( void ) { return 1; }
unsigned long f5 = Fact (); template<> unsigned long Fact ( void ) { return 3*Fact (); } template<> unsigned long Fact ( void ) { return 2*Fact (); } template<> unsigned long Fact ( void ) { return 1; } Факториал на этапе компиляции (4) Процесс остановился. Все работает.
typename Имеем класс: class C { public: typedef int SomeType; }; Определяем шаблонную функцию: template void f() { T::SomeType *var; // будет ли это компилироваться? } Компилятор может это распознать или как объявление указателя на тип T::SomeType или как умножение T::SomeType на var. Следовательно, будет ошибка компиляции.
typename (2) Таким образом при использовании в шаблоне члена класса мы должны явно указывать, что из себя представляет этот член: –Если это тип, то перед ним надо указывать typename typename T::SomeType *var; –Если это не тип, то typename указывать не нужно.
Домашнее задание 1 Реализовать вычисление факториала на этапе компиляции с помощью следующей конструкции: template struct Fact { const static int value =... }; 14
Домашнее задание 2 На основе реализованного на прошлой лекции класса Stack написать решение задачи о Ханойских башнях. Каждый стержень – это экземпляр класса Stack. Параметр шаблона стека T – это класс Disk, имеющий поле diameter. Программа должна уметь выдавать пользователю содержание всех трёх стержней в любой указанный пользователем момент времени (пример: через 50 перекладываний). 15