Язык программирования C++ от Страуструпа

       

Разрешение перегрузки для шаблонной функции


К параметрам шаблонной функции нельзя применять никаких преобразований

типа. Вместо этого при необходимости создаются новые варианты функции:

template<class T> 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<class T> 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<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,int)


  char m2 = max(c,d);              // max(char,char)

  int m3 = max(a,c);               // ошибка: невозможно

                                   // создать max(int,char)

}

Поскольку до генерации функции по шаблону не применяется никаких преобразований типа (правило [2]), последний вызов в этом примере нельзя разрешить как max(a,int(c)). Это может сделать сам пользователь, явно описав функцию max(int,int). Тогда вступает в силу правило [3]:

template<class T>

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<class T1, class T2>

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)

Если в шаблоне для функции, которая может иметь множество параметров с различными арифметическими типами, используются два параметра, то в результате по шаблону будет порождаться слишком большое число определений разных функций. Более разумно добиваться преобразования типа, явно описав функцию с нужными типами.


Содержание раздела