~vector() { delete[] elem; } // деструктор
T& operator[](int n) { return elem[n]; } // доступ: возвращает
// ссылку
const T& operator[](int n) const { return elem[n]; }
int size() const { return sz; } // текущий размер
int capacity() const { return space; }
void resize(int newsize); // увеличивает вектор
void push_back(const T& d);
void reserve(int newalloc);
};
Это определение класса
vector
совпадает с определением класса
vector
, содержащего элементы типа
double
(см. раздел 19.2.6), за исключением того, что ключевое слово
double
теперь заменено шаблонным параметром
T
. Этот шаблонный класс
vector
можно использовать следующим образом:
vector<double> vd; // T — double
vector<int> vi; // T — int
vector<double*> vpd; // T — double*
vector< vector<int> > vvi; // T — vector<int>, в котором T — int
Можно просто считать, что компилятор генерирует класс конкретного типа (соответствующего шаблонному аргументу), подставляя его вместо шаблонного параметра. Например, когда компилятор видит в программе конструкцию
vector<char>
, он генерирует примерно такой код:
class vector_char {
int sz; // размер
char* elem; // указатель на элементы
int space; // размер + свободная память
public:
vector_char();
explicit vector_char(int s);
vector_char(const vector_char&); // копирующий конструктор
vector_char& operator=(const vector_char &); // копирующее
// присваивание
~vector_char (); // деструктор
char& operator[] (int n); // доступ: возвращает ссылку
const char& operator[] (int n) const;
int size() const; // текущий размер
int capacity() const;
void resize(int newsize); // увеличение
void push_back(const char& d);
void reserve(int newalloc);
};
Для класса
vector<double>
компилятор генерирует аналог класса
vector
, содержащий элементы типа
double
(см. раздел 19.2.6), используя соответствующее внутреннее имя, подходящее по смыслу конструкции
vector<double>
).
Иногда шаблонный класс называют
порождающим типом (type generator). Процесс генерирования типов (классов) с помощью шаблонного класса по заданным шаблонным аргументам называется
специализацией (specialization) или
конкретизацией шаблона (template instantiation). Например, классы
vector<char>
и
vector<Poly_line*>
называются специализациями класса
vector
. В простых ситуациях, например при работе с классом
vector
, конкретизация не вызывает затруднений. В более общих и запутанных ситуациях конкретизация шаблона очень сильно усложняется. К счастью для пользователей шаблонов, вся эта сложность обрушивается только на разработчика компилятора.
Конкретизация шаблона (генерирование шаблонных специализаций) осуществляется на этапе компиляции или редактирования связей, а не во время выполнения программы.
Естественно, шаблонный класс может иметь функции-члены. Рассмотрим пример.
void fct(vector<string>& v)
{
int n = v.size();
v.push_back("Norah");
// ...
}
При вызове такой функции-члена шаблонного класса компилятор генерирует соответствующую конкретную функцию. Например, когда компилятор видит вызов
v.push_back("Norah"), он генерирует функцию
void vector<string>::push_back(const string& d) { /* ... */ }
используя шаблонное определение
template<class T> void vector<T>::push_back(const T& d) { /* ... */ };
Итак, вызову
v.push_back("Norah")
соответствует конкретная функция. Иначе говоря, если вам нужна функция с конкретным типом аргумента, компилятор сам напишет ее, основываясь на вашем шаблоне.
Вместо префикса
template<class T>
можно использовать префикс
template <typename T>
. Эти две конструкции означают одно и то же, но некоторые программисты все же предпочитают использовать ключевое слово
typename
, “потому, что оно яснее, и потому, что никто не подумает, что оно запрещает использовать встроенные типы, например тип
int
, в качестве шаблонного аргумента”. Мы считаем, что ключевое слово
class
уже означает “тип”, поэтому никакой разницы между этими конструкциями нет. Кроме того, слово
class
короче.