Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
КРАТКИЙ ОБЗОР С.doc
Скачиваний:
1
Добавлен:
26.10.2018
Размер:
2.11 Mб
Скачать

8.4.5 Введение операций с помощью параметров шаблонного класса

      Возможны ситуации, когда неявность связи между шаблонной функцией       sort() и шаблонным классом Comparator создает трудности. Неявную       связь легко упустить из виду и в то же время разобраться в ней       может быть непросто. Кроме того, поскольку эта связь "встроена"       в функцию sort(), невозможно использовать эту функцию для       сортировки векторов одного типа, если операция сравнения рассчитана       на другой тип (см. упражнение 3 в $$8.9). Поместив функцию sort()       в класс, мы можем явно задавать связь с классом Comparator:       template<class T, class Comp> class Sort {       public:       static void sort(Vector<T>&);       };       Не хочется повторять тип элемента, и это можно не делать, если       использовать typedef в шаблоне Comparator:       template<class T> class Comparator {       public:       typedef T T; // определение Comparator<T>::T       static int lessthan(T& a, T& b) {       return a < b;       }       // ...       };       В специальном варианте для указателей на строки это определение       выглядит так:       class Comparator<char*> {       public:       typedef char* T;       static int lessthan(T a, T b) {       return strcmp(a,b) < 0;       }       // ...       };       После этих изменений можно убрать параметр, задающий тип элемента,       из класса Sort:       template<class T, class Comp> class Sort {       public:       static void sort(Vector<T>&);       };       Теперь можно использовать сортировку так:       void f(Vector<int>& vi,       Vector<String>& vc,       Vector<int>& vi2,       Vector<char*>& vs)       {       Sort< int,Comparator<int> >::sort(vi);       Sort< String,Comparator<String> >:sort(vc);       Sort< int,Comparator<int> >::sort(vi2);       Sort< char*,Comparator<char*> >::sort(vs);       }       и определить функцию sort() следующим образом:       template<class T, class Comp>       void Sort<T,Comp>::sort(Vector<T>& v)       {       for (int i=0; i<n-1; i++)       for (int j=n-1; i<j; j--)       if (Comp::lessthan(v[j],v[j-1])) {       T temp = v[j];       v[j] = v[j-1];       v[j-1] = temp;       }       }       Последний вариант ярко демонстрирует как можно соединять в одну       программу отдельные ее части. Этот пример можно еще больше       упростить, если использовать класс сравнителя (Comp) в качестве       единственного параметра шаблона. В этом случае в определениях класса       Sort и функции Sort::sort() тип элемента будет обозначаться как Comp::T.

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

      К параметрам шаблонной функции нельзя применять никаких преобразований       типа. Вместо этого при необходимости создаются новые варианты       функции:       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)       Если в шаблоне для функции, которая       может иметь множество параметров с различными арифметическими       типами, используются два параметра, то в результате по шаблону будет       порождаться слишком большое число определений разных функций.       Более разумно добиваться преобразования типа, явно описав функцию       с нужными типами.