Байланысты: Бьерн Страуструп. Язык программирования С . М Бином, 2011
8.5 Разрешение перегрузки для шаблонной функции К параметрам шаблонной функции нельзя применять никаких преобразований типа. Вместо этого при
необходимости создаются новые варианты функции:
template T sqrt(t);
void f(int i, double d, complex z)
{
complex z1 = sqrt(i); // sqrt(int)
complex z2 = sqrt(d); // sqrt(double)
complex z3 = sqrt(z); // sqrt(complex)
//
...
}
Здесь для всех трех типов параметров будет создаваться по шаблону своя функция sqrt. Если
пользователь захочет чего-нибудь иного, например вызвать sqrt(double), задавая параметр int, нужно
использовать явное преобразование типа:
template T sqrt(T);
void f(int i, double d, complex z)
{
complex z1 = sqrt(double(i)); // sqrt(double)
complex z2 = sqrt(d); // sqrt(double)
complex z3 = sqrt(z); // sqrt(complex)
//
...
}
В этом примере по шаблону будут создаваться определения только для sqrt(double) и sqrt(complex).
Шаблонная функция может перегружаться как простой, так и шаблонной функцией того же имени.
Разрешение перегрузки как шаблонных, так и обычных функций с одинаковыми именами происходит за
три шага. Эти правила слишком строгие, и, по всей видимости будут ослаблены, чтобы разрешить
преобразования ссылок и указателей, а, возможно, и другие стандартные преобразования. Как обычно,
при таких преобразованиях будет действовать контроль однозначности.
[1] Найти функцию с точным сопоставлением параметров ($$R.13.2); если такая есть, вызвать ее.
[2] Найти шаблон типа, по которому можно создать вызываемую функцию с точным
сопоставлением параметров; если такая есть, вызвать ее.
[3] Попробовать правила разрешения для обычных функций ($$r13.2); если функция найдена по
этим правилам, вызвать ее, иначе вызов является ошибкой.
В любом случае, если на первом шаге найдено более одной функции, вызов считается неоднозначным
и является ошибкой. Например:
template 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,int)
char m2 = max(c,d);
// max(char,char)
int m3 = max(a,c);
// ошибка: невозможно
//
создать max(int,char)
}
Поскольку до генерации функции по шаблону не применяется никаких преобразований типа (правило
Бьерн Страуструп.
Язык программирования С++
222
[2]), последний вызов в этом примере нельзя разрешить как max(a,int(c)). Это может сделать сам
пользователь, явно описав функцию max(int,int). Тогда вступает в силу правило [3]:
template T max(T a, T b) { return a>b?a:b; }
int max(int,int);
void f(int a, int b, char c, char d)
{
int m1 = max(a,b); // max(int,int)
char m2 = max(c,d); // max(char,char)
int m3 = max(a,c); // max(int,int)
}
Программисту не нужно давать определение функции max(int,int), оно по умолчанию будет создано по
шаблону.
Можно определить шаблон max так, чтобы сработал первоначальный вариант нашего примера:
template T1 max(T1 a, T2 b) { return a>b?a:b; };
void f(int a, int b, char c, char d)
{
int m1 = max(a,b);
// int max(int,int)
char m2 = max(c,d);
// char max(char,char)
int m3 = max(a,c);
// max(int,char)
}
Однако, в С и С++ правила для встроенных типов и операций над ними таковы, что использовать
подобный шаблон с двумя параметрами может быть совсем непросто. Так, может оказаться неверно
задавать тип результата функции как первый параметр (T1), или, по крайней мере, это может привести
к неожиданному результату, например для вызова
max(c,i); // char max(char,int)
Если в шаблоне для функции, которая может иметь множество параметров с различными
арифметическими типами, используются два параметра, то в результате по шаблону будет
порождаться слишком большое число определений разных функций. Более разумно добиваться
преобразования типа, явно описав функцию с нужными типами.