Справочное руководство по C++ - Бьярн Страустрап
Шрифт:
Интервал:
Закладка:
При вызове шаблонной функции параметры шаблона типа не задаются явно, вместо этого применяется правило разрешения неопределенности перегруженных функций. Рассмотрим пример:
vector‹complex› cv(100);
vector‹int› ci(200);
void f(vector‹complex›& cv, vector‹int›& ci)
{
sort(cv); // вызывается sort(vector‹complex›)
sort(ci); // вызывается sort(vector‹int›)
}
Шаблонная функция может быть перегружена как обычными, так и шаблонными функциями с тем же именем. Для разрешения неопределенности шаблонных и обычных функций с одним и тем же именем надо последовательно пройти три шага:
[1] Попытаться найти точно сопоставимую вызову (§R.13.2) функцию, и если она найдена, вызвать ее.
[2] Попытаться найти шаблон типа для функций, по которому можно создать точно сопоставимую с рассматриваемым вызовом функцию. Если удалось найти, то вызвать функцию.
[3] Попытаться применить обычное правило разрешения неопределенности перегруженных функций (§R.13.2). Если с его помощью функция найдена, вызвать ее.
Если не найдено сопоставимой функции, вызов является ошибочным.
Если уже на первом шаге найдено более одного кандидата, сопоставимого с данной функцией, то вызов также считается неоднозначным и ошибочным.
Успешное выполнение шага [2] приведет к созданию некоторой шаблонной функции с параметрами (§R.14.5), типы которых точно сопоставятся с типами параметров, указанных в вызове. В этом случае недопустимо расхождение даже за счет тривиальных преобразований (§R.13.2).
Такие же действия применяются для сопоставления типов указателей на функции (§R.13.3).
Рассмотрим пример:
template‹class T› T max(T a, T b) { return a›b ? a : b; };
void f(int a, int b, char c, char d)
{
int m1 = max(a,b); // max(int a, int b)
char m2 = max(c,d); // max(char c, char b)
int m3 = max(a,c); // ошибка: нельзя создать max(int,char)
}
Добавив к этому примеру описание
int max(int,int);
можно разрешить неопределенность для третьего вызова, поскольку теперь задана функция, которая после стандартного преобразования char в int, может сопоставиться с вызовом max(a,c).
Определение шаблона типа для функции используется для создания различных вариантов шаблона типа. Для вызова определенного варианта достаточно лишь описания шаблона типа.
Каждый параметр-шаблона-типа, который приведен в списке-параметров-шаблона-типа должен обязательно использоваться при задании типов параметров в шаблоне типа для функции.
template‹class T› T* create(); //ошибка
template‹class T›
void f() {// ошибка
T a;
//…
}
Все параметры-шаблона-типа, приведенные в шаблоне типа для функции, должны быть параметрами-типа.
R.14.5 Описания и определения
Для каждого имени шаблона типа в программе должно существовать только одно определение. Описаний может быть несколько. Определение используется для создания специальных шаблонных классов и шаблонных функций, которые будут соответствовать шаблону типа.
Конструкция имя-шаблонного-класса вводит описание шаблонного класса.
Вызов шаблонной функции или взятие ее адреса вводит описание шаблонной функции. Для вызова или взятия адреса шаблонной функции в языке существует особое соглашение: имя шаблонной функции используется точно так же как имя обычной функции. Описание функции с таким же именем, как у шаблонной функции, и с сопоставимыми типами параметров, вводит описание специальной шаблонной функции.
Если для выполнения некоторых операций требуется определение специального шаблонного класса или специальной шаблонной функции, и если такого определения в программе нет, то оно будет создано.
Определение обычной (нешаблонной) функции с типом, который точно сопоставляется с типом из описания шаблонной функции, считается определением специальной шаблонной функции. Рассмотрим пример:
template‹class T› void sort(vector‹T›& v) {/*… */}
void sort(vector‹char*›& v) {/*… */}
Здесь определение функции sort будет использоваться для той функции из семейства sort, которая сопоставляется при вызове с типом параметра vector‹char*›. Для других типов vector будет создаваться соответствующее им определение функции по шаблону типа.
Можно определить класс, который задает шаблонный класс, например:
template‹class T› class stream {/*… */};
class stream‹char› {/*… */};
Здесь описание класса будет использоваться в качестве определения потока символов (stream‹char›). Другие потоки будут управляться с помощью шаблонных функций, создаваемых по шаблону типа для функций.
Пока не появится описание шаблона типа для класса, никакие операции, которым требуется определенный класс, не могут быть произведены над шаблонным классом. После этого специальный шаблонный класс будет считаться определенным, причем сразу же перед первым глобальным описанием, использующим его имя.
R.14.6 Функции-члены шаблонов типа
Функция-член шаблонного класса считается неявной шаблонной функцией, а параметры шаблона типа для ее класса - ее шаблонными параметрами. Приведем пример, в котором описаны три шаблона типа для функции:
template‹class T› class vector {
T* v;
int sz;
public:
vector(int);
T& operator[](int);
T& elem(int i) { return v[i]; }
//…
};
Функцию, выполняющую индексацию, можно определить следующим образом:
template‹class T› T& vector‹T›::operator[](int i)
{
if (i‹0 || sz›=i) error("vector: range error");
return v[i];
}
Шаблонный параметр для vector‹T›::operator[]() будет задаваться тем типом vector, к которому применяется операция индексации.
vector‹int› v1(20);
vector‹complex› v2(30);
v1[3] = 7; // vector‹int›::operator[]()
v2[3] = complex(7,8); // vector‹complex›::operator[]()
R.14.7 Друзья
Функция-друг для шаблона типа не является неявной шаблонной функцией, например:
template‹class T› class task {
//…
friend void next_time();
friend task‹T›* preempt(task‹T›*);
friend task* prmt(task*); // ошибка
//…
};
Здесь функция next_time() становится другом всех классов task, а каждый класс task имеет в качестве друга функцию preempt() c соответствующими типами параметров. Функцию preempt() можно определить как шаблон типа.
template‹class T›
task‹T›* preempt(task‹T›* t) {/*… */}
Описание функции prmt() является ошибочным, поскольку типа task не существует, а есть только специальные шаблонные типы task‹int›, task‹record›, и т.д.
R.14.8 Статические члены и переменные
Для каждого шаблонного класса или функции, создаваемых по шаблону типа, образуется своя копия статических переменных или членов. Рассмотрим пример:
template‹class T› class X {
static T s;
//…
};
X‹int› aa;
X‹char*› bb;
Здесь в классе X‹int› есть статический член типа int, а в классе X‹char› есть статический член типа char*.
Аналогично, в приведенном ниже примере, функция f(int*) имеет статический член s типа int, а функция f(char**) имеет статический член типа char**:
template‹class T› f(T* p)
{
static T s;
//…
}
void g(int a, char* b)
{
f(&a);
f(&b);
}
R.15 Обработка особых ситуаций
R.15.1 Обработка особых ситуаций
При обработке особых ситуаций в ходе выполнения программы информация и управление передаются из некоторой точки обработчику особых ситуаций. Обработчик находится в цепочке выполненных вызовов функций. Управление обработчику передается с помощью выражения-запуска, которое может быть только в проверяемом-блоке обработчика или в функции, вызванной из проверяемого-блока.
проверяемый-блок:
try составной-оператор список-обработчиков
список-обработчиков:
обработчик список-обработчиков opt
обработчик:
catch ( описание-особой-ситуации ) составной-оператор
описание-особой-ситуации:
список-спецификаций-типа описатель
список-спецификаций-типа абстрактный-описатель
список-спецификаций-типа
…
выражение-запуска:
throw выражение opt
Конструкция проверяемый-блок является оператором (§R.6), а выражение-запуска - унарным выражением типа void (§R.5). Иногда выражение-запуска называют "точкой запуска", а про функцию, в которой встретилось выражение-запуска, говорят, что она "запускает особую ситуацию. Часть программы, которой передается управление из точки запуска называется обработчиком.