10.6. Явная специализация шаблона А

10.6. Явная специализация шаблона А

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

// обобщенное определение шаблона

template class T

T max( T t1, T t2 ) {

return ( t1 t2 ? t1 : t2 );

}

Когда этот шаблон конкретизируется с аргументом типа const char*, то обобщенное определение оказывается семантически некорректным, если мы интерпретируем каждый аргумент как строку символов в смысле языка C, а не как указатель на символ. В этом случае необходимо предоставить специализированное определение для конкретизации шаблона.

Явное определение специализации – это такое определение, в котором за ключевым словом template следует пара угловых скобок , а за ними – определение специализированного шаблона. Здесь указывается имя шаблона, аргументы, для которых он специализируется, список параметров функции и ее тело. В следующем примере для max(const char*, const char*) определена явная специализация:

#include cstring

// явная специализация для const char*:

// имеет приоритет над конкретизацией шаблона

// по обобщенному определению

typedef const char *PCC;

template PCC max PCC ( PCC s1, PCC s2 ) {

return ( strcmp( s1, s2 ) 0 ? s1 : s2 );

Поскольку имеется явная специализация, шаблон не будет конкретизирован с типом const char* при вызове в программе функции max(const char*, const char*). При любом обращении к max() с двумя аргументами типа const char* работает специализированное определение. Для любых других обращений функция сначала конкретизируется по обобщенному определению шаблона, а затем вызывается. Вот как это выглядит:

#include iostream

// здесь должно быть определение шаблона функции max()

// и его специализации для аргументов const char*

int main() {

// вызов конкретизированной функции: int max int ( int, int );

int i = max( 10, 5 );

// вызов явной специализации:

// const char* max const char* ( const char*, const char* );

const char *p = max( "hello", "world" );

p endl;

return 0;

}

Можно объявлять явную специализацию шаблона функции, не определяя ее. Например, для функции 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 );

В следующем примере шаблон функции sum() явно специализирован:

template class T1, class T2, class T3

T1 sum( T2 op1, T3 op2 );

// объявления явных специализаций

// ошибка: аргумент шаблона для T1 не может быть выведен;

// он должен быть задан явно

template double sum( float, float );

// правильно: аргумент для T1 задан явно,

// T2 и T3 выводятся и оказываются равными float

template double sumdouble( float, float );

// правильно: все аргументы заданы явно

template int sumint,char( char, char );

Пропуск части template в объявлении явной специализации не всегда является ошибкой. Например:

// обобщенное определение шаблона

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 ) { /* ... */ }

В предыдущем примере конкретизация max(const char*, const char*) предшествует объявлению явной специализации. Поэтому компилятор имеет право предположить, что функция должна быть конкретизирована по обобщенному определению шаблона. Однако в программе не может одновременно существовать явная специализация и экземпляр, конкретизированный по тому же шаблону с тем же множеством аргументов. Когда в исходном файле после конкретизации встречается явная специализация max(const char*, 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 объявлена явная специализация, и при обращении к 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():

// --------- 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" );

// ....

}

Упражнение 10.10

Определите шаблон функции count() для подсчета числа появлений некоторого значения в массиве. Напишите вызывающую программу. Последовательно передайте в ней массив значений типа double, int и сhar. Напишите специализированный экземпляр шаблона count() для обработки строк.

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг

Выбор шаблона

Из книги Microsoft Office автора Леонтьев Виталий Петрович

Выбор шаблона Как мы уже говорили, Publisher рассчитан на работу в «пошаговом» режиме – мы как бы собираем будущую публикацию по кусочкам. А еще точнее – создаем ее на основе одного из бесчисленных шаблонов. На компакт-диске с Publisher хранится более полутора тысяч шаблонов


Специализация шаблонных функций – членов шаблонного класса

Из книги Вариации на тему STL. Адаптер обобщенного указателя на функцию-член класса автора Гусаров Михаил

Специализация шаблонных функций – членов шаблонного класса К сожалению, вышеприведенный код не будет компилироваться на компиляторах, не поддерживающих специализацию шаблонов-функций – членов шаблонов классов.ПРИМЕЧАНИЕ К таким относятся, например, gcc-2.95 и gcc-2.96


Частичная специализация

Из книги Симуляция частичной специализации автора Кузнецов Павел

Частичная специализация К сожалению, не все компиляторы поддерживают частичную специализацию шаблонных классов.ПРИМЕЧАНИЕ К таким относится и Microsoft Visual C++ 6.0/7.0 Для решения этой проблемы можно использовать паттерн «traits», специфичный для C++. К сожалению, он не сможет


Частичная специализация по виду аргумента шаблона

Из книги Справочное руководство по C++ автора Страустрап Бьярн

Частичная специализация по виду аргумента шаблона Одним из аспектов частичной специализации является возможность специализировать шаблон по виду аргумента, например, предоставить общую для всех указателей специализацию шаблона:template‹class T› class С { //…}; template‹class T› class


Явная реализация интерфейса

Из книги Эффективное делопроизводство автора Пташинский Владимир Сергеевич

Явная реализация интерфейса В определении IDraw3D мы были вынуждены назвать наш единственный метод Draw3D(), чтобы избежать конфликта с абстрактным методом Draw(), определенным в базовом классе Shape. Такое определение интерфейса вполне допустимо, но более естественным именем для


Понятие шаблона

Из книги Создание шаблонов Joomla автора Автор неизвестен

Понятие шаблона Для упрощения работы по созданию и форматированию текстов, стандартизации расположения и оформления текста, графики, типизации операций обработки документов и прочего используются шаблоны документов. Пакет Microsoft Office дает различные определения шаблона


Структура шаблона

Из книги Программирование на языке Ruby [Идеология языка, теория и практика применения] автора Фултон Хэл

Структура шаблона Помимо специального заголовка, для шаблона необходима структура. Создать структуру можно при помощи таблиц или тегов <div>. Далее мы опишем создание табличной версии структуры. Если в Dremweaver все еще активирован режим разметки (layout mode), закройте


11.2.2. Специализация отдельного объекта

Из книги XSLT автора Хольцнер Стивен

11.2.2. Специализация отдельного объекта Я солипсист и, признаться, удивлен, что большинство из нас таковыми не являются. Из письма, полученного Бертраном Расселом В большинстве объектно-ориентированных языков все объекты одного класса ведут себя одинаково. Класс — это


Создание шаблона

Из книги Технология XSLT автора Валиков Алексей Николаевич

Создание шаблона В главе 2 для выбора узлов в planets.xml и преобразования этого документа в HTML я создал основной шаблон. Шаблоны в таблицах стилей создаются при помощи элементов <xsl:template>, задающих правила для требуемых преобразований. Мы создали шаблон, находивший корневой


Тело шаблона

Из книги Как сделать свой сайт и заработать на нем. Практическое пособие для начинающих по заработку в Интернете автора Мухутдинов Евгений

Тело шаблона Фактически, элемент xsl:template, определяющий шаблонное правило, задает не более чем условия, при которых это правило должно выполняться. Конкретные же действия и инструкции, которые должны быть исполнены, определяются содержимым элемента xsl:template и составляют


10.1. Определение шаблона функции

Из книги автора

10.1. Определение шаблона функции Иногда может показаться, что сильно типизированный язык создает препятствия для реализации совсем простых функций. Например, хотя следующий алгоритм функции min() тривиален, сильная типизация требует, чтобы его разновидности были


10.2. Конкретизация шаблона функции

Из книги автора

10.2. Конкретизация шаблона функции Шаблон функции описывает, как следует строить конкретные функции, если задано множество фактических типов или значений. Процесс конструирования называется конкретизацией шаблона. Выполняется он неявно, как побочный эффект вызова


10.11. Пример шаблона функции

Из книги автора

10.11. Пример шаблона функции В этом разделе приводится пример, показывающий, как можно определять и использовать шаблоны функций. Здесь определяется шаблон sort(), который затем применяется для сортировки элементов массива. Сам массив представлен шаблоном класса Array (см.