Выбрать главу

Это можно сделать, если воспользоваться явным определением специализации, где после ключевого слова template идет пара угловых скобок , а за ней - определение специализации члена класса. В приведенном примере для функций-членов min() и max() класса Queue, конкретизированного из шаблона, определены явные специализации:

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

template LongDouble Queue::min()

{

assert( ! is_empty() );

LongDouble min_val = front-item;

for ( QueueItem *pq = front-next; pq != 0; pq = pq-next )

if ( pq-item.compareLess( min_val ) )

min_val = pq-item;

return min_val;

}

template LongDouble QueueLongDouble ::max()

{

assert( ! is_empty() );

LongDouble max_val = front-item;

for ( QueueItem *pq = front-next; pq != 0; pq = pq-next )

if ( max_val.compareLess( pq-item ) )

max_val = pq-item;

return max_val;

}

Хотя тип класса Queue конкретизируется по шаблону, в каждом объекте этого типа используются специализированные функции-члены min() и max() - не те, что конкретизируются по обобщенным определениям этих функций в шаблоне класса Queue.

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

// объявления явных специализаций функций-членов

template LongDouble Queue LongDouble ::min();

template LongDouble QueueLongDouble ::max();

Поместив эти объявления в заголовочный файл, а соответствующие определения - в исходный, мы можем организовать код так же, как и для определений функций-членов обычного класса.

Иногда определение всего шаблона оказывается непригодным для конкретизации некоторым типом. В таком случае программист может специализировать шаблон класса целиком. Напишем полное определение класса Queue:

// QueueLD.h: определяет специализацию класса QueueLongDouble

#include "Queue.h"

template QueueLongDouble {

QueueLongDouble ();

~QueueLongDouble ();

LongDouble& remove();

void add( const LongDouble & );

bool is_empty() const;

LongDouble min();

LongDouble max();

private:

// Некоторая реализация

};

Явную специализацию шаблона класса можно определять только после того, как общий шаблон уже был объявлен (хотя и не обязательно определен). Иными словами, должно быть известно, что специализируемое имя обозначает шаблон класса. Если в приведенном примере не включить заголовочный файл Queue.h перед определением явной специализации шаблона, компилятор выдаст сообщение об ошибке, указывая, что Queue - это не имя шаблона.

Если мы определяем специализацию всего шаблона класса, то должны определить также все без исключения функции-члены и статические данные-члены. Определения членов из общего шаблона никогда не используются для создания определений членов явной специализации: множества членов этих шаблонов могут различаться. Чтобы предоставить определение явной специализации для типа класса Queue, придется определить не только функции-члены min() и max(), но и все остальные.

Если класс специализируется целиком, лексемы template помещаются только перед определением явной специализации всего шаблона:

#include "QueueLD.h"

// определяет функцию-член min()

// из специализированного шаблона класса

LongDouble QueueLongDouble::min() { }

Класс не может в одних файлах конкретизироваться из общего определения шаблона, а в других - из специализированного, если задано одно и то же множество аргументов. Например, специализацию шаблона QueueItem необходимо объявлять в каждом файле, где она используется:

// ---- File1.C ----

#include "Queue.h"

void ReadIn( QueueLongDouble *pq ) {

// использование pq-add()

// приводит к конкретизации QueueItemLongDouble

}

// ---- File2.C ----

#include "QueueLD.h"

void ReadIn( QueueLongDouble * )ошибку не обнаружат: заголовочный файл QueueLD.h

следует включать во все файлы, где используется Queue,

причем до первого использования.

16.10. Частичные специализации шаблонов классов A

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