С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 88
Текст из файла (страница 88)
Предположим, чтомы хотели бы определить специализацию шаблона min(), чтобы она пользовалась// функция сравнения объектов SmallInt// возвращает true, если parm1 меньше parm2функцией compareLess() для сравнения двух подобных объектов. Вот ее объявление:bool compareLess( const SmallInt &parm1, const SmallInt &parm2 );Как должно выглядеть определение этой функции? Чтобы ответить на этот вопрос,необходимо познакомиться с определением класса SmallInt более подробно. Данныйкласс позволяет определять объекты, которые хранят тот же диапазон значений, что и 8разрядный тип unsigned char, т.е.
от 0 до 255. Дополнительная функциональностьсостоит в том, что класс перехватывает ошибки переполнения и потери значимости. Вовсем остальном он должен вести себя точно так же, как unsigned char. ОпределениеSmallInt выглядит следующим образом:513С++ для начинающихclass SmallInt {public:SmallInt( int ival ) : value( ival ) {}friend bool compareLess( const SmallInt &, const SmallInt & );private:int value;// член};В этом классе есть один закрытый член value, в котором хранится значение объекта типа// конструктор класса SmallIntSmallInt. Класс также содержит конструктор с параметром ival:SmallInt( int ival ) : value( ival ) {}Его единственное назначение – инициализировать член класса value значением ival.Вот теперь можно ответить на ранее поставленный вопрос: как должна быть определенафункция compareLess()? Она будет сравнивать члены value переданных ей аргументов// возвращает true, если parm1 меньше parm2bool compareLess( const SmallInt &parm1, const SmallInt &parm2 ) {return parm1.value < parm2.value;типа SmallInt:}Заметим, однако, что член value является закрытым.
Как может глобальная функцияобратиться к закрытому члену, не нарушив инкапсуляции класса SmallInt и не вызвавтем самым ошибку компиляции? Если вы посмотрите на определение класса SmallInt,то заметите, что глобальная функция compareLess() объявлена как дружественная(friend). Если функция объявлена таким образом, то ей доступны закрытые членыкласса. (Друзья классов рассматриваются в разделе 15.2.)Теперь мы готовы определить специализацию шаблона min(). Она следующим образом// специализация min() для массива объектов SmallInttemplate<> SmallInt min<smallInt>( SmallInt* array, int size ){SmallInt min_val = array[0];for (int i = 1; i < size; ++i)// при сравнении используется функция compareLess()if ( compareLess( array[i], min_val ) )min_val = array[i];print( "Minimum value found: " );print( min_val );return min_val;использует функцию compareLess().}514С++ для начинающих// ---- primer.h ---namespace cplusplus_primer {// определение шаблона скрыто в пространстве именtemplate <class Type>Type min( Type* array, int size ) { /* ...
*/ }}// ---- user.h ---class SmallInt { /* ... */ };void print( const SmallInt & );bool compareLess( const SmallInt &, const SmallInt & );// ---- user.C ---#include <primer.h>#include "user.h"// ошибка: это не специализация для cplusplus_primer::min()template<> SmallInt min<smallInt>( SmallInt* array, int size ){ /* ... */ }Где мы должны объявить эту специализацию? Предположим, что здесь:// ...К сожалению, этот код не работает.
Явная специализация шаблона функции должна бытьобъявлена в том пространстве имен, где определен порождающий шаблон. Поэтому мыобязаны определить специализацию min() в пространстве cplusplus_primer. В нашейпрограмме это можно сделать двумя способами.Напомним, что определения пространства имен не обязательно непрерывны. Мы можем// ---- user.C ---#include <primer.h>#include "user.h"namespace cplusplus_primer {// специализация для cplusplus_primer::min()template<> SmallInt min<smallInt>( SmallInt* array, int size ){ /* ...
*/ }}SmallInt asi[4];int main() {// задать значения элементов массива asi с помощью функции-члена set()using cplusplus_primer::min;// using-объявлениеint size = sizeof(asi) / sizeof(SmallInt);// конкретизируется min(SmallInt*,int)min( &asi[0], size );повторно открыть пространство имен cplusplus_primer для добавления специализации:}Можно определить специализацию так, как мы определяем любой другой членпространства имен вне определения самого пространства: квалифицировав имя членаименем объемлющего пространства.515С++ для начинающих516// ---- user.C ---#include <primer.h>#include "user.h"// специализация для cplusplus_primer::min()// имя специализации квалифицируетсяnamespace {template<> SmallInt cplusplus_primer::min<smallInt>( SmallInt* array, int size ){ /* ... */ }// ...Если вы, пользуясь библиотекой, содержащей определения шаблонов, захотите написатьих специализации, то должны будете удостовериться, что их определения помещены в тоже пространство имен, что и определения исходных шаблонов.Упражнение 10.15Поместим содержимое заголовочного файла <exercise.h> из упражнения 10.14 впространство имен cplusplus_primer.
Как надо изменить функцию main(), чтобы онамогла конкретизировать шаблон max(), находящийся в cplusplus_primer?Упражнение 10.16Снова обращаясь к упражнению 10.14, предположим, что содержимое заголовочногофайла <exercise.h> помещено в пространство имен cplusplus_primer. Допустим, мыхотим специализировать шаблон функции max() для массивов объектов классаLongDouble. Нужно, чтобы специализация шаблона использовала функцию// функция сравнения объектов класса LongDouble// возвращает true, если parm1 больше parm2bool compareGreater( const LongDouble &parm1,compareGreater() для сравнения двух объектов класса LongDouble, объявленную как:const LongDouble &parm2 );class LongDouble {public:LongDouble(double dval) : value(ival) {}friend bool compareGreater( const LongDouble &,const LongDouble & );private:double value;Определение класса LongDouble выглядит следующим образом:};Напишите определение функции compareGreater() и специализацию max(), в которойэта функция используется.
Напишите также функцию main(), которая задает элементымассива ad, а затем вызывает специализацию max(), доставляющую его максимальныйэлемент. Значения, которыми инициализируется массив ad, должны быть полученычтением из стандартного ввода cin.С++ для начинающих10.11. Пример шаблона функцииВ этом разделе приводится пример, показывающий, как можно определять ииспользовать шаблоны функций. Здесь определяется шаблон sort(), который затемприменяется для сортировки элементов массива. Сам массив представлен шаблономкласса Array (см. раздел 2.5).
Таким образом, шаблоном sort() можно пользоваться длясортировки массивов элементов любого типа.В главе 6 мы видели, что в стандартной библиотеке C++ определен контейнерный типvector, который ведет себя во многом аналогично типу Array. В главе 12рассматриваются обобщенные алгоритмы, способные манипулировать контейнерами,описанными в главе 6. Один из таких алгоритмов, sort(), служит для сортировкисодержимого вектора. В этом разделе мы определим собственный “обобщенный алгоритмsort()” для манипулирования классом Array, упрощенной версии алгоритма изстандартной библиотеки C++.template <class elemType>void sort( Array<elemType> &array, int low, int high ) {if ( low < high ) {int lo = low;int hi = high + 1;elemType elem = array[lo];for (;;) {while ( min( array[++lo], elem ) != elem && lo < high ) ;while ( min( array[--hi], elem ) == elem && hi > low ) ;if (lo < hi)swap( array, lo, hi );else break;}swap( array, low, hi );sort( array, low, hi-1 );sort( array, hi+1, high );}Шаблон функции sort() для шаблона класса Array определен следующим образом:}В sort() используются две вспомогательные функции: min() и swap().
Обе они должныопределяться как шаблоны, чтобы иметь возможность обрабатывать любые типыфактических аргументов, с которыми может быть конкретизирован шаблон sort().min() определена как шаблон функции для поиска минимального из двух значенийtemplate <class Type>Type min( Type a, Type b ) {return a < b ? a : b;любого типа:}swap() – шаблон функции для перестановки двух элементов массива любого типа:517С++ для начинающихtemplate <class elemType>void swap( Array<elemType> &array, int i, int j ){elemType tmp = array[ i ];array[ i ] = array[ j ];array[ j ] = tmp;}Убедиться в том, что функция sort() действительно работает, можно с помощьюотображения содержимого массива после сортировки.
Поскольку функция display()должна обрабатывать любой массив, конкретизированный из шаблона класса Array, ее#include <iostream>template <class elemType>void display( Array<elemType> &array ){ //формат отображения: < 0 1 2 3 4 5 >cout << "< ";for ( int ix = 0; ix < array.size(); ++ix )cout << array[ix] << " ";cout << ">\n";тоже следует определить как шаблон:}В этом примере мы пользуемся моделью компиляции с включением и помещаемшаблоны всех функций в заголовочный файл Array.h вслед за объявлением шаблонакласса Array.Следующий шаг – написание функции для тестирования этих шаблонов. В sort()поочередно передаются массивы элементов типа double, типа int и массив строк.
Воттекст программы:518С++ для начинающих#include <iostream>#include <string>#include "Array.h"double da[10] = {26.7, 5.7, 37.7, 1.7, 61.7, 11.7, 59.7,15.7, 48.7, 19.7 };int ia[16] = {503, 87, 512, 61, 908, 170, 897, 275, 653,426, 154, 509, 612, 677, 765, 703 };string sa[11] = {"a", "heavy", "snow", "was", "falling", "when","they", "left", "the", "police", "station" };int main() {// вызвать конструктор для инициализации arrdArray<double> arrd( da, sizeof(da)/sizeof(da[0]) );// вызвать конструктор для инициализации arriArray<int> arri( ia, sizeof(ia)/sizeof(ia[0]) );// вызвать конструктор для инициализации arrsArray<string> arrs( sa, sizeof(sa)/sizeof(sa[0]) );cout << "sort array of doubles (size == "<< arrd.size() << ")" << endl;sort(arrd, 0, arrd.size()-1 );display(arrd);cout << "sort array of ints (size == "<< arri.size() << ")" << endl;sort(arri, 0, arri.size()-1 );display(arri);cout << "sort array of strings (size == "<< arrs.size() << ")" << endl;sort(arrs, 0, arrs.size()-1 );display(arrs);return 0;}Если скомпилировать и запустить программу, то она напечатает следующее (эти строкиискусственно разбиты на небольшие части):sort array of doubles (size == 10)< 1.7 5.7 11.7 14.9 15.7 19.7 26.737.7 48.7 59.7 61.7 >sort array of ints (size == 16)< 61 87 154 170 275 426 503 509 512612 653 677 703 765 897 908 >sort array of strings (size == 11)< "a" "falling" "heavy" "left" "police" "snow""station" "the" "they" "was" "when" >519С++ для начинающихВ числе обобщенных алгоритмов, имеющихся в стандартной библиотеке C++ (и в главе12), вы найдете также функции min() и swap().