В примере из предыдущего раздела объекты Comparator на самом деле никак не использовались в вычислениях. Это просто "искусственные" параметры, нужные для правильного контроля типов. Введение таких параметров достаточно общий и полезный прием, хотя и не слишком красивый. Однако, если объект используется только для передачи операции (как и было в нашем случае), т.е. в вызываемой функции не используется ни значение, ни адрес объекта, то можно вместо этого передавать операцию неявно:
template<class T> void sort(Vector<T>& v) { unsigned n = v.size();
for (int i=0; i<n-1; i++) 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; } }
В результате мы приходим к первоначальному варианту использования 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* тривиальна и может использоваться и в других ситуациях.