Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Специализации шаблонов классов A



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

template < class Type>

class Queue {

//...

public:

Type min();

Type max();

//...

};

 

// найти минимальное значение в очереди Queue

template < class Type>

Type Queue< Type>:: 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 Queue< Type>:: 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< (). Если же этого оператора нет, то попытка применить min() к очереди приведет к ошибке компиляции в том месте, где вызывается несуществующий оператор сравнения. (Аналогичная проблема существует и в max(), только касается оператора operator> ()).

Предположим, что шаблон класса Queue нужно конкретизировать таким типом:

class LongSouble {

public:

LongDouble( double dbval ): value( dval ) { }

bool compareLess( const LongDouble & );

private:

double value;

};

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

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

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

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

template< > LongDouble Queue< LongDouble>:: 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 Queue< LongDouble>:: 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< LongDouble> конкретизируется по шаблону, в каждом объекте этого типа используются специализированные функции-члены min() и max() – не те, что конкретизируются по обобщенным определениям этих функций в шаблоне класса Queue.

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

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

template < > LongDouble Queue< LongDouble>:: min();

template < > LongDouble Queue< LongDouble>:: max();

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

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

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

#include " Queue.h"

 

template< > Queue< LongDouble> {

Queue< LongDouble> ();

~Queue< LongDouble> ();

 

LongDouble& remove();

void add( const LongDouble & );

bool is_empty() const;

LongDouble min();

LongDouble max();

private:

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

};

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

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

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

#include " QueueLD.h"

 

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

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

LongDouble Queue< LongDouble>:: min() { }

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

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

#include " Queue.h"

 

void ReadIn( Queue< LongDouble> *pq ) {

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

// приводит к конкретизации QueueItem< LongDouble>

}

 

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

#include " QueueLD.h"

 

void ReadIn( Queue< LongDouble> * );

 

int main() {

// используется определение специализации для Queue< LongDouble>

Queue< LongDouble> *qld = new Queue< LongDouble>;

 

ReadIn( qld );

//...

}

Эта программа некорректна, хотя большинство компиляторов ошибку не обнаружат: заголовочный файл QueueLD.h следует включать во все файлы, где используется Queue< LongDouble>, причем до первого использования.


Поделиться:



Последнее изменение этой страницы: 2019-04-09; Просмотров: 290; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.022 с.)
Главная | Случайная страница | Обратная связь