Программирование >>  Поддержка объектно-ориентированного программирования 

1 ... 67 68 69 [ 70 ] 71 72 73 ... 120


В результате мы приходим к первоначальному варианту использования sort():

void f(Vector<int>& vi,

Vector<String>& vc, Vector<int>& vi2, Vector<char*>& vs)

sort(vi); sort(Vector<int>&);

sort(vc); sort(Vector<String>&);

sort(vi2); sort(Vector<int>&);

sort(vs); sort(Vector<char*>&);

Основное преимущество этого варианта, как и двух предыдущих, по сравнению с исходным вариантом в том, что часть программы, занятая собственно сортировкой, отделена от частей, в которых находятся такие операции, работающие с элементами, как, например lessthan. Необходимость подобного разделения растет с ростом программы, и особенный интерес это разделение представляет при проектировании библиотек. Здесь создатель библиотеки не может знать типы параметров шаблона, а пользователи не знают (или не хотят знать) специфику используемых в шаблоне алгоритмов. В частности, если бы в функции sort() использовался более сложный, оптимизированный и рассчитанный на коммерческое применение алгоритм, пользователь не очень бы стремился написать свою особую версию для типа char*, как это было сделано в $$8.4.1. Хотя реализация класса Comparator для специального случая char* тривиальна и может использоваться и в других ситуациях.

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

Возможны ситуации, когда неявность связи между шаблонной функцией sort() и шаблонным классом Comparator создает трудности. Неявную связь легко упустить из виду и в то же время разобраться в ней может быть непросто. Кроме того, поскольку эта связь встроена в функцию sort(), невозможно использовать эту функцию для сортировки векторов одного типа, если операция сравнения рассчитана на другой тип (см. упражнение 3 в $$8.9). Поместив функцию sort() в класс, мы можем явно задавать связь с классом Comparator:

Отметим, что включение в шаблон класса Comparator как параметра гарантирует, что функция lessthan будет реализовываться подстановкой. В частности, это полезно, если в шаблонной функции используется несколько функций, а не одна операция сравнения, и особенно это полезно, когда эти функции зависят от хранящихся в том же объекте данных.

8.4.4 Неявная передача операций

В примере из предыдущего раздела объекты Comparator на самом деле никак не использовались в вычислениях. Это просто искусственные параметры, нужные для правильного контроля типов. Введение таких параметров достаточно общий и полезный прием, хотя и не слишком красивый. Однако, если объект используется только для передачи операции (как и было в нашем случае), т.е. в вызываемой функции не используется ни значение, ни адрес объекта, то можно вместо этого передавать операцию неявно:

template<class T> void sort(Vector<T>& v)

unsigned n = v.sizeO; for (int i=0; i<n-1;

for (int j=n-1; i<j; j-- )

if (Comparator<T>::lessthan(v[j],v[j-1])) { меняем местами v[j] и v[j -1] T temp = v[j];

v[j] = v[j-1];

v[j-1] = temp;



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;

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] Попробовать правила разрешения для обычных функций ($$r1 3.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)

Поскольку до генерации функции по шаблону не применяется никаких преобразований типа (правило



1 ... 67 68 69 [ 70 ] 71 72 73 ... 120

© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика