В следующем примере явно объявляется конкретизация шаблона Queueint, в котором запрашивается конкретизация аргументом int шаблона класса Queue:
#include "Queue.h"
// явное объявление конкретизации
template class Queueint;
Если шаблон класса конкретизируется явно, то явно конкретизируются и все его члены, причем тем же типом аргумента. Следовательно, в файле, где встречается явное объявление, должно присутствовать не только определение шаблона, но и определения всех его членов. В противном случае выдается сообщение об ошибке:
template class Type
class Queue;
// ошибка: шаблон Queue и его члены не определены
template class Queueint;
Если в некотором исходном файле встречается явное объявление конкретизации, то что произойдет в других файлах, где используется такой же конкретизированный шаблон? Как сказать компилятору, что явное объявление имеется в другом файле и что при употреблении шаблона класса или его членов в этом файле конкретизировать ничего не надо?
Здесь, как и при использовании шаблонов функций (см. раздел 10.5.3), необходимо применить опцию компилятора, подавляющую неявные конкретизации. Эта опция вынуждает компилятор предполагать, что все конкретизации шаблонов будут объявляться явно.
Упражнение 16.9
Куда бы вы поместили определения функций-членов и статических данных-членов своих шаблонов классов, если имеющийся у вас компилятор поддерживает модель компиляции с разделением? Объясните почему.
Упражнение 16.10
Имеется шаблон класса Screen, разработанный в упражнениях из предыдущих разделов (в том числе функции-члены, определенные в упражнении 16.5 из раздела 16.3, и статические члены, определенные в упражнении 16.7 из раздела 16.5). Организуйте программу так, чтобы воспользоваться преимуществами модели компиляции с разделением.
16.9. Специализации шаблонов классов A
Прежде чем приступать к рассмотрению специализаций шаблонов классов и причин, по которым в них может возникнуть надобность, добавим в шаблон Queue функции-члены min() и max(). Они будут обходить все элементы очереди и искать среди них соответственно минимальное и максимальное значения (правильнее, конечно, использовать для этой цели обобщенные алгоритмы min() и max(), представленные в главе 12, но мы определим эти функции как члены шаблона Queue, чтобы познакомиться со специализациями.)
template class Type
class Queue {
// ...
public:
Type min();
Type max();
// ...
};
// найти минимальное значение в очереди Queue
template class Type
Type QueueType::min()
{
assert( ! is_empty() );
Type min_val = front-item;
for ( QueueItem *pq = front-next; pq != 0; pq = pq-next )
if ( pq-item min_val )
min_val = pq-item;
return min_val;
}
// найти максимальное значение в очереди Queue
template class Type
Type QueueType::max()
{
assert( ! is_empty() );
Type max_val = front-item;
for ( QueueItem *pq = front-next; pq != 0; pq = pq-next )
if ( pq-item max_val )
max_val = pq-item;
return max_val;
}
Следующая инструкция в функции-члене min() сравнивает два элемента очереди Queue:
pq-item min_val
Здесь неявно присутствует требование к типам, которыми может конкретизироваться шаблон класса Queue: такой тип должен либо иметь возможность пользоваться предопределенным оператором “меньше” для встроенных типов, либо быть классом, в котором определен оператор operator()).
Предположим, что шаблон класса Queue нужно конкретизировать таким типом:
class LongSouble {
public:
LongDouble( double dbval ) : value( dval ) { }
bool compareLess( const LongDouble & );
private:
double value;
};
Но в этом классе нет оператора operator(), позволяющего сравнивать два значения типа LongDouble, поэтому использовать для очереди типа Queue функции-члены min() и max() нельзя. Одним из решений этой проблемы может стать определение глобальных operator(), в которых для сравнения значений типа Queue используется функция-член compareLess. Эти глобальные операторы вызывались бы из min() и max() автоматически при сравнении объектов из очереди.