С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 84
Текст из файла (страница 84)
Например,// объявление явной специализации шаблона функциидля функции max(const char*, const char*) она объявляется так:template< > PCC max< PCC >( PCC, PCC );При объявлении или определении явной специализации шаблона функции нельзяопускать слово template и следующую за ним пару скобок <>. Кроме того, в объявлении// ошибка: неправильные объявления специализации// отсутствует template<>PCC max< PCC >( PCC, PCC );// отсутствует список параметровспециализации обязательно должен быть список параметров функции:template<> PCC max< PCC >;Однако здесь можно опускать задание аргументов шаблона, если они выводятся из// правильно: аргумент шаблона const char* выводится из типов параметровформальных параметров функции:template<> PCC max( PCC, PCC );template <class T1, class T2, class T3>T1 sum( T2 op1, T3 op2 );// объявления явных специализаций// ошибка: аргумент шаблона для T1 не может быть выведен;// он должен быть задан явноtemplate<> double sum( float, float );// правильно: аргумент для T1 задан явно,// T2 и T3 выводятся и оказываются равными floattemplate<> double sum<double>( float, float );// правильно: все аргументы заданы явноВ следующем примере шаблон функции sum() явно специализирован:template<> int sum<int,char>( char, char );Пропуск части template<> в объявлении явной специализации не всегда являетсяошибкой.
Например:492С++ для начинающих// обобщенное определение шаблонаtemplate <class T>T max( T t1, T t2 ) { /* ... */ }// правильно: обычное объявление функцииconst char* max( const char*, const char*);Однако эта инструкция не является специализацией шаблона функции. Здесь простообъявляется обычная функция с типом возвращаемого значения и списком параметров,которые соответствуют полученным при конкретизации шаблона. Объявление обычнойфункции, являющееся конкретизацией шаблона, не считается ошибкой.Так почему бы просто не объявить обычную функцию? Как было показано в разделе 10.3,для преобразования фактического аргумента функции, конкретизированной по шаблону,в соответствующий формальный параметр в случае, когда этот аргумент принимаетучастие в выводе аргумента шаблона, может быть применено лишь ограниченноемножество преобразований типов.
Точно так же обстоит дело и в ситуации, когда шаблонфункции специализируется явно: к фактическим аргументам функции при этом тожеприменимо лишь ограниченное множество преобразований. Явные специализации непомогают обойти соответствующие ограничения. Если мы хотим выйти за их пределы, тодолжны определить обычную функцию вместо специализации шаблона. (В разделе 10.8этот вопрос рассматривается более подробно; там же показано, как работает разрешениеперегруженной функции для вызова, который соответствует как обычной функции, так иэкземпляру, конкретизированному из шаблона.)Явную специализацию можно объявлять даже тогда, когда специализируемый шаблонобъявлен, но не определен.
В предыдущем примере шаблон функции sum() лишьобъявлен к моменту специализации. Хотя определение шаблона не обязательно,объявление все же требуется. То, что sum() – шаблон, должно быть известно до того, какэто имя может быть специализировано.Такое объявление должно быть видимо до его использования в исходном файле.#include <iostream>#include <cstring>// обобщенное определение шаблонаtemplate <class T>T max( T t1, T t2 ) { /* ...
*/ }int main() {// конкретизация функции// const char* max< const char* >( const char*, const char* );const char *p = max( "hello", "world" );cout << "p: " << p << endl;return 0;}// некорректная программа: явная специализация const char *:// имеет приоритет над обобщенным определением шаблонаtypedef const char *PCC;Например:template<> PCC max< PCC >(PCC s1, PCC s2 ) { /* ... */ }493С++ для начинающихВ предыдущем примере конкретизация max(const char*, const char*) предшествуетобъявлению явной специализации.
Поэтому компилятор имеет право предположить, чтофункция должна быть конкретизирована по обобщенному определению шаблона. Однаков программе не может одновременно существовать явная специализация и экземпляр,конкретизированный по тому же шаблону с тем же множеством аргументов. Когда висходном файле после конкретизации встречается явная специализация max(constchar*, const char*), компилятор выдает сообщение об ошибке.Если программа состоит из нескольких файлов, то объявление явной специализациишаблона должно быть видимо в каждом файле, в котором она используется.
Неразрешается в одних файлах конкретизировать шаблон функции по обобщенномуопределению, а в других специализировать с тем же множеством аргументов. Рассмотрим// --------- max.h ------// обобщенное определение шаблонаtemplate <class Type>Type max( Type t1, Type t2 ) { /* ... */ }// --------- File1.C ------#include <iostream>#include "max.h"void another();int main() {// конкретизация функции// const char* max< const char* >( const char*, const char* );const char *p = max( "hello", "world" );cout << "p: " << p << endl;another();return 0;}// --------- File2.C ------#include <iostream>#include <cstring>#include "max.h"// явная специализация шаблона для const char*typedef const char *PCC;template<> PCC max< PCC >( PCC s1, PCC s2 ) { /* ... */ }void another() {// явная специализация// const char* max< const char* >( const char*, const char* );const char *p = max( "hi", "again" );cout << " p: " << p << endl;return 0;следующий пример:}Эта программа состоит из двух файлов.
В файле File1.C нет объявления явнойспециализации max(const char*, const char*). Вместо этого шаблон функцииконкретизируется из обобщенного определения. В файле File2.C объявлена явная494С++ для начинающих495специализация, и при обращении к max("hi", "again") именно она и вызывается.Поскольку в одной и той же программе функция max(const char*, const char*) токонкретизируется по шаблону, то специализируется явно, компилятор считает программунекорректной. Для исправления этого объявление явной специализации шаблона должнопредшествовать вызову функции max(const char*, const char*) в файле File1.C.Чтобы избежать таких ошибок и гарантировать, что объявление явной специализациишаблона max(const char*, const char*) внесено в каждый файл, где используетсяшаблон функции max() с аргументами типа const char*, это объявление следуетпоместить в заголовочный файл "max.h" и включать его во все исходные файлы, в// --------- max.h ------// обобщенное определение шаблонаtemplate <class Type>Type max( Type t1, Type t2 ) { /* ...
*/ }// объявление явной специализации шаблона для const char*typedef const char *PCC;template<> PCC max< PCC >( PCC s1, PCC s2 );// --------- File1.C ------#include <iostream>#include "max.h"void another();int main() {// специализация// const char* max< const char* >( const char*, const char* );const char *p = max( "hello", "world" );// ....которых используется шаблон max():}Упражнение 10.10Определите шаблон функции count() для подсчета числа появлений некоторогозначения в массиве. Напишите вызывающую программу. Последовательно передайте вней массив значений типа double, int и сhar.
Напишите специализированныйэкземпляр шаблона count() для обработки строк.10.7. Перегрузка шаблонов функций АШаблон функции может быть перегружен.перегруженных объявления для шаблона min():В следующемпримере естьтриС++ для начинающих496// определение шаблона класса Array// (см. раздел 2.4)template <typename Type>class Array( /* ... */ };// три объявления шаблона функции min()template <typename Type>Type min( const Array<Type>&, int ); // #1template <typename Type>Type min( const Type*, int ); // #2template <typename Type>Type min( Type, Type ); // #3Следующее определение main() иллюстрирует, как могут вызываться три объявленных#include <cmath>int main(){Array<int> iA(1024);int ia[1024];// конкретизация класса// Type == int; min( const Array<int>&, int )int ival0 = min( iA, 1024 );// Type == int; min( const int*, int )int ival1 = min( ia, 1024 );// Type == double; min( double, double )double dval0 = min( sqrt( iA[0] ), sqrt( ia[0] ) );return 0;таким образом функции:}Разумеется, тот факт, что три перегруженных шаблона функции успешно объявлены, неозначает, что они могут быть также успешно вызваны.
Такие шаблоны могут приводить кнеоднозначности при вызове конкретизированного шаблона. Например, для следующегоtemplate <typename T>определения шаблона min5()int min5( T, T ) { /* ... */ }функция не конкретизируется по шаблону, если min5() вызывается с аргументамиразных типов; при этом процесс вывода заканчивается с ошибкой, поскольку изфактических аргументов функции выводятся два разных типа для T.С++ для начинающихint i;unsigned int ui;// правильно: для T выведен тип intmin5( 1024, i );// вывод аргументов шаблона заканчивается с ошибкой:// для T можно вывести два разных типаmin5 ( i, ui );Для разрешения второго вызова можно было бы перегрузить min5(), допустив дваtemplate <typename T, typename U>различных типа аргументов:int min5( T, U );// правильно: int min5( int, usigned int )При следующем обращении производится конкретизация этого шаблона функции:min5( i, ui );// ошибка: неоднозначность: две возможных конкретизации//из min5( T, T ) и min5( T, U )К сожалению, теперь стал неоднозначным предыдущий вызов:min5( 1024, i );Второе объявление min5() допускает наличие у функции аргументов различных типов,но не требует этого.
В нашем случае и T, и U типа int. Оба объявления шаблонов могутбыть конкретизированы вызовом, в котором два аргумента функции имеют один и тот жетип. Единственный способ указать, какой шаблон более предпочтителен, устранив темсамым неоднозначность, – явно задать его аргументы. (О явном задании аргументов// правильно: конкретизация из min5( T, U )шаблона см. раздел 10.4.) Например:min5<int, int>( 1024, i );Однако в этом случае мы можем обойтись без перегрузки шаблона функции.