Слайд 2
Шаблоны функций
template void sort(vector&);
void f (vector&
vi, vector &vs){
sort< int >(vi); //sort(vector&)
sort(vs);
//sort(vector&)
}
При вызове шаблона функции аргументы могут однозначно определять какая версия шаблона используется. В этом случае говорят что аргументы шаблона функции выводятся по аргументам функции
Компилятор может вывести аргументы как являющиеся типами так и обычные при условии, что список аргументов функции однозначно идентифицирует набор аргументов шаблона
Слайд 3
Пример
template T& lookup(Buffer&b, const
char *p);
class Record {
const char[12];
//…
};
Record& f(Buffer &buf, const
char *p)
{
// вызвать lookup(), где T-Record, i -128
return lookup(buf,p);
}
//Иногда требуется указать аргумент явно:
template T* create();
void f() {
vector v;
int *p = create();
}
Слайд 4
Перегрузка шаблонов функций
template T sqrt(T);
template
T> complex sqrt( complex);
double sqrt(double);
void f(complex z){
sqrt(2); //sqrt
(int)
sqrt(2.0); //sqrt(double)
sqrt(z); //sqrt (complex)
}
Слайд 5
Правила разрешения перегрузки
Ищется набор специализаций шаблонов функций, принимается
решение какие аргументы были бы использованы, если бы в
текущей области видимости не было других шаблонов функций и обычных функций с тем же именем (в примере –sqrt(complex) и sqrt>(complex)
Если могут быть вызваны два шаблона функции и один из них более специализирован чем другой, на последующих этапах только он и рассматривается.(Для нашего примера предпочтение отдается sqrt(complex) по отношению к sqrt(complex) )
Слайд 6
Правила разрешения перегрузки
Разрешается перегрузка для данного набора функций,
а также для любых обычных функций (в соответствии с
правилами разрешения перегрузки для обычных функций). Если аргументы функции шаблона были определены путем выведения по фактическим аргументам шаблона, к ним нельзя применять «продвижение», стандартные и определяемые пользователем преобразования типа. (в нашем примере sqrt(int) является точным соответствием и поэтому ей отдается предпочтение по отношению к sqrt(double)
Если и обычная функция и специализация подходят одинаково хорошо, то предпочтение отдается обычной функции (sqrt(double) а не sqrt(double)
Слайд 7
Правила разрешения перегрузки
Если ни одного соответствия не найдено
или процесс оканчивается нахождением двух или более одинаково хорошо
подходящими вариантами, то выдается соответствующая ошибка компиляции
template T max (T, T);
const int s = 7;
void doIt() {
max(1,2); // max(1,2)
max('a','b'); // max('a','b')
max(2.7,4.9); // max (2.7,4.9)
max(s,7); //max(int(s), 7) –тривиальное преобр.
max('a',1); //error –неоднозначность, стандартные
//преобразования не применяются
max(2.7,4); //error –неоднозначность
}
Слайд 8
Разрешение неоднозначности
Явная квалификация:
max(‘a’,1); // max (int(‘a’), 1)
max(2.7,4) //
max(2.7, double(4))
Добавление подходящих объявлений
inline int max (int i, int
j) {return max(i, j);}
inline double max (double d1, double d2) {
return max(d1, d2);
}
inline double max (int i, double d) {
return max(i, d);}
inline double max (double d, int i) {
return max(d, i);}
void f () {
max(‘a’,1); //max (int ('a'), 1)
max(2.7, 4); //max (2.7, double (4))
}
Слайд 9
Перегрузка и наследование
Правила перегрузки гарантируют, что функции шаблоны
корректно взаимодействуют с наследованием:
template class B {/*…*/};
template
class D: public B {/*…*/};
template void f( B* );
void g (B *pb, D *pd) {
f(pb); //f (pb)
f(pd); //f (static_cast*>(pd));
}
Слайд 10
Дополнительные аспекты разрешения
Аргумент функции, не использующийся при
выведении параметра шаблона рассматривается точно также, как аргумент функции,
не являющейся шаблоном, и к нему применяются обычные правила преобразования для аргумента функции при перегрузке обычных функций:
template int get_nth (C& p, int n);
class Index {
public:
operator int();
};
void f (vector &v, short s, Index i) {
int i1 = get_nth (v, 2); //точное соответсвие
int i2 = get_nth (v, s); //short в int
int i3 = get_nth (v, i); //Index в int
}
Слайд 11
Использование аргументов шаблона для выведения алгоритма
template
class C=Cmp >
int compare (const String& s1, const String&
s2) {
for (int i=0; i if(!C::eq(s1[i], s2[i]))
return C::lt(s1[i], s2[i]) ? -1 : 1;
return s1.length() – s2.length();
}
//Определяем класс для правил сравнения
template class Cmp {
public:
static int eq (T a, T b) { return a==b; }
static int lt (T a, T b) { return a < b;}
};
String str1, str2;
//compare > (str1, str2);
compare (str1,str2);
compare > (wstr1, wstr2);
Слайд 12
Специализация
По умолчанию шаблон предоставляет единое определение генерируемого типа
которое используется для всех возможных аргументов шаблона
Но иногда может
возникнуть необходимость в уточнении определения для определенных категорий аргументов, например:
«Если аргументом является указатель, то используй эту реализацию, если нет –используй ту»
«Выдай сообщение об ошибке если аргументом шаблона не является указатель на объект класса, производного от My_base»
Многие подобные проблемы решаются обеспечением альтернативных определений шаблона , выбор которых при инстанцировании осуществляет компилятор на основании аргументов шаблона указанных при его использовании
Слайд 13
Класс Vector – кандидат на специализацию
template
class Vector {
T* v;
int sz;
public:
Vector();
Vector(int);
T& elem(int i) {return v[i];}
T&
operator[] (int i);
void swap(Vector&);
};
Vector vi;
Vector vps;
Vector vs;
Vector vpc;
Vector vpn;
Слайд 14
Специализация Vector
template class Vector {
void **p;
int
sz
public:
Vector();
Vector(int);
void* &elem(int i) {return p[i];}
void* &operator[] (int i)
{return elem(i);}
void swap(Vector&);
//…
};
Полная специализация –отсутствует параметр шаблона, который бы следовало задавать или который бы выводился при инстанцировании при использовании специализации
Слайд 15
Частичная специализация
template class Vector :
private Vector {
public:
typedef Vector Base;
Vector():Base() {}
explicit Vector(int): Base(i) {}
T*
&elem(int i) {
return static_cast (Base::elem());
}
T* &operator[] (int i) {
return static_cast (Base::operator[](i));
}
//…
};
Vector vps; // -это , T –shape
Vector vppi; // -это , T –int*
Слайд 16
Правила объявлений для специализаций
Общий шаблон должен быть объявлен
прежде любой специализации
Если программист специализирует где-нибудь шаблон, то эта
специализация должна быть в области видимости при каждом использовании шаблона с типом, для которого он был специализирован
Все специализации шаблона должны быть объявлены в том же пространстве имен что и сам шаблон
Одна специализация считается более специализированной чем другая если каждый список аргументов соответствующий образцу первой специализации соответствует и второй специализации, но не наоборот
Более специализированной версии будет отдаваться предпочтение в объявлениях объектов, указателей, а также при разрешении перегрузки
Слайд 17
Пример, специализация функций
//общий шаблон
template class Vector;
//частичная специализация
template
class Vector;
//полная специализация
template class Vector;
template
T> bool less (T a, T b) {return a
//Для функций поддерживается только полная специализация
template<> bool less<> (const char *a, const char *b) {
return strcmp(a,b)<0;
}
//Вторые пустые <> можно опустить:
template<> bool less(const char *a, const char *b) {
return strcmp(a,b)<0;
}
Слайд 18
Наследование и шаблоны
Наследование реализации и обеспечение типобезопасного использования,
например:
template class Vector : private Vector{/*…*/};
Уточнение
существующего шаблона (класса) или обобщение набора операций, например:
template class CheckedVector : public Vector{/*…*/};
template class Basic_ops {
public:
bool operator==(const C&) const;
bool operator!=(const C&) const;
const C& derived() {return static_cast*this;}
};
Слайд 19
Пример использования
template class Math_container :public Basic_ops
Math_container > {
public:
size_t size() const;
T& operator[] (size_t);
const T& operator[](size_t)
const;
//…
};
template
bool Basic_ops::operator==(const C& a) const {
if (derived().size() != a.size()) return false;
for (int i = 0; i if (derived()[i]!=a[i]) return false;
return true;
}
Слайд 20
Организация исходного кода
Ранние компиляторы:
Объявления и определения шаблонов размещаются
в заголовочном файле
Заголовочный файл включается во все единицы трансляции
где используются данные шаблоны
Современные компиляторы:
Заголовочный файл включает только объявления шаблона, определение иго структуры и его специализаций (без определения членов шаблона)
Определения членов шаблона размещаются в некоторой единице компиляции и предваряются ключевым словом export по аналогии со встраиваемыми (inline)функциями
Слайд 21
Примеры кода
Первый вариант
//file out.h :
#include
template
T> void out (const T &t) {std::err
и iostream
#include ”out.h”
Второй вариант
//file out.h :
template void out (const T&);
//file out.cpp
#include
export template void out(const T &t) {…}
//file user2.cpp – отсутствует зависимость от iostream
#include “out.h”