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