if ( val1 != val2 )

cerr "ошибка реализации!" endl;

return 0;

}

Во всех конкретизациях Q значения empty одинаковы, но при ссылке на empty необходимо указывать, какому именно экземпляру Q принадлежит перечисление.

Упражнение 16.8

Определите класс List и вложенный в него ListItem из раздела 13.10 как шаблоны. Реализуйте аналогичные определения для ассоциированных членов класса.

<p>16.7. Шаблоны-члены</p>

Шаблон функции или класса может быть членом обычного класса или шаблона класса. Определение шаблона-члена похоже на определение шаблона: ему предшествует ключевое слово template, за которым идет список параметров:

template class T

class Queue {

private:

// шаблон класса-члена

template class Type class CL

{

Type member;

T mem;

};

// ...

public:

// шаблон функции-члена

template class Iter

void assign( Iter first, Iter last )

{

while ( ! is_empty() )

remove(); // вызывается Queue T ::remove()

for ( ; first != last; ++first )

add( *first ); // вызывается Queue T ::add( const T & )

}

}

(Отметим, что шаблоны-члены не поддерживаются компиляторами, написанными до принятия стандарта C++. Эта возможность была добавлена в язык для поддержки реализации абстрактных контейнерных типов, представленных в главе 6.)

Объявление шаблона-члена имеет собственные параметры. Например, у шаблона класса CL есть параметр Type, а у шаблона функции assign() - параметр Iter. Помимо этого, в определении шаблона-члена могут использоваться параметры объемлющего шаблона класса. Например, у шаблона CL есть член типа T, представляющего параметр включающего шаблона Queue.

Объявление шаблона-члена в шаблоне класса Queue означает, что конкретизация Queue потенциально может содержать бесконечное число различных вложенных классов CL функций-членов assign(). Так, конкретизированный экземпляр Queue включает вложенные типы:

Queueint::CLchar

Queueint ::CLstring

и вложенные функции:

void Queueint ::assign( int *, int * )

void Queueint ::assign( vectorint ::iterator,

vectorint ::iterator )

Для шаблона-члена действуют те же правила доступа, что и для других членов класса. Так как шаблон CL является закрытым членом шаблона Queue, то лишь функции-члены и друзья Queue могут ссылаться на его конкретизации. С другой стороны, шаблон функции assign() объявлен открытым членом и, значит, доступен во всей программе.

Шаблон-член конкретизируется при его использовании в программе. Например, assign() конкретизируется в момент обращения к ней из main():

int main()

{

// конкретизация Queueint

Queueint qi;

// конкретизация Queueint::assign( int *, int * )

int ai[4] = { 0, 3, 6, 9 };

qi.assign( ai, ai + 4 );

// конкретизация Queueint ::assign( vectorint ::iterator,

// vectorint ::iterator )

vectorint vi( ai, ai + 4 );

qi.assign( vi.begin(), vi.end() );

}

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

В функции main() шаблон-член assign() сначала конкретизируется типом int*, что позволяет поместить в qi содержимое массива элементов типа int. Затем шаблон-член конкретизируется типом vectorint::iterator - это дает возможность поместить в очередь qi содержимое вектора элементов типа int. Контейнер, содержимое которого помещается в очередь, не обязательно должен состоять из элементов типа int. Разрешен любой тип, который приводится к int. Чтобы понять, почему это так, еще раз посмотрим на определение assign():

template class Iter

void assign( Iter first, Iter last )

{

Перейти на страницу:

Похожие книги