Внутри шаблона класса можно также объявлять перечисления и определять типы (с помощью typedef):
template class Type, int size
class Buffer:
public:
enum Buf_vals { last = size-1, Buf_size };
typedef Type BufType;
BufType array[ size ];
// ...
}
Вместо того чтобы явно включать член Buf_size, в шаблоне класса Buffer объявляется перечисление с двумя элементами, которые инициализируются значением параметра шаблона. Например, объявление
Bufferint, 512 small_buf;
устанавливает Buf_size в 512, а last - в 511. Аналогично
Bufferint, 1024 medium_buf;
устанавливает Buf_size в 1024, а last - в 1023.
Открытый вложенный тип разрешается использовать и вне определения объемлющего класса. Однако вызывающая программа может ссылаться лишь на конкретизированные экземпляры подобного типа (или элементов вложенного перечисления). В таком случае имени вложенного типа должно предшествовать имя конкретизированного шаблона класса:
// ошибка: какая конкретизация Buffer?
Buffer::Buf_vals bfv0;
Bufferint,512::Buf_vals bfv1; // правильно
Это правило применимо и тогда, когда во вложенном типе не используются параметры включающего шаблона:
template class T class Q {
public:
enum QA { empty, full }; // не зависит от параметров
QA status;
// ...
};
#include iostream
int main() {
Qdouble qd;
Qint qi;
qd.status = Q::empty; // ошибка: какая конкретизация Q?
qd.status = Qdouble ::empty; // правильно
int val1 = Qdouble ::empty;
int val2 = Qint ::empty;
if ( val1 != val2 )
cerr "ошибка реализации!" endl;
return 0;
}
Во всех конкретизациях Q значения empty одинаковы, но при ссылке на empty необходимо указывать, какому именно экземпляру Q принадлежит перечисление.
Упражнение 16.8
Определите класс List и вложенный в него ListItem из раздела 13.10 как шаблоны. Реализуйте аналогичные определения для ассоциированных членов класса.
16.7. Шаблоны-члены
Шаблон функции или класса может быть членом обычного класса или шаблона класса. Определение шаблона-члена похоже на определение шаблона: ему предшествует ключевое слово 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 * )