Программирование >>  Разработка устойчивых систем 

1 ... 62 63 64 [ 65 ] 66 67 68 ... 196


Полная специализация для void* tempiate<>

class Stack<void *> { void** data: std::size t count; std::size t capacity: enum {INIT = 5}; public: Stack О {

count = 0:

capacity = INIT;

data = new void*[INIT]:

void push(void* const & t) { if (count == capacity) { std::size t newCapacity = 2*capacity: void** newData = new void*[newCapacity]: std::memcpy(newData, data. count*sizeof(void*)); delete [] data; data = newData: capacity = newCapacity;

assert(count < capacity): data[count++] = t;

void popO { assert(count > 0): --count;

void* topO const { assert(count > 0): return data[count-l];

std::size t sizeO const {return count;}

Неполная специализация для других указателей tempiate<class Т>

class Stack<T*> : private Stack<void *> { typedef Stack<void *> Base:

public:

void push(T* const & t) {Base::push(t):} void popO {Base: ;pop();}

T* topO const {return static cast<T*>(Base::top());} std::size t sizeO {return Base::size();}

#endif NOBLOATJ /:-

Этот простой стек автоматически расширяется по мере заполнения. Специализация для void* является полной, на что указывает префикс templateo (с пустым списком параметров шаблона). Как уже упоминалось, в специализации шаблона класса должны быть реализованы все функции класса. Экономия достигается для всех остальных указателей. Неполная специализация для прочих типов указателей создается закрытым наследованием от Stacl void*>, поскольку мы всего лишь задействуем Stacl void*> в реализации и не желаем предоставлять пользователю прямой доступ к его интерфейсу. Функции класса в каждой специализации ограничиваются простым перенаправлением вызова к соответствующим функциям



Разрешение имен

Встречая в программе идентификатор, компилятор должен сначала определить тип и область видимости (а в случае переменных - срок жизни) для сущности, пред-

Stacl void*>. Таким образом, при каждой специализации шаблона для типа указателя, отличного от void*, размер сгенерированного кода составит лишь ничтожную часть того размера, который бы потребовался при использовании основного шаблона. Ниже приводится тестовая программа:

: C05:NobloatTest.cpp #inc1ude <1ostream> linclude <stnng> linclude Nobloat.h using namespace std;

tempiate<c1ass StackType> void emptyTheStack(StackType& stk) { while (stk.SizeO > 0) {

cout stk.topO endl;

stk.popO;

Перегрузка для emptyTheStack (не специализация!) tempiate<class T>

void emptyTheStack(Stack<T*>& stk) { while (stk.SizeO > 0) { cout *stk.top() endl; Stk.popO:

int mainO {

Stack<int> si:

sl.push(l):

sl.push(2):

emptyTheStack(si):

Stack<int *> s2:

int i - 3:

int j - 4:

s2.push(&i):

s2.push(&j):

emptyTheStack(s2): } III-

Для удобства в программу включены два шаблона функции emptyTheStack. Поскольку шаблоны функций не поддерживают неполной специализации, мы предоставляем перегруженные шаблоны. Вторая версия emptyTheStack более специализирована по сравнению с первой, поэтому она выбирается при каждом использовании типов указателей. В программе создаются три специализации шаблона класса: Stack<int>, Stack<void*> и Stack<int*>. Специализация Stack<void*> создается косвенно, поскольку Stack<int*> является производной от нее. Программа, в которой задействованы специализации для разных типов указателей, обеспечивает существенную экономию по сравнению с простым использованием одного шаблона Stack.



Так называемое правило Кёнига, по имени Эндрю Кёнига (Andrew Koenig), который первым предложил эту методику комитету по стандартизации С++. ADL применяется как при наличии шаблонов, так и без них.

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

Имена в шаблонах

в первой фазе компилятор разбирает определение шаблона, ищет очевидные синтаксические ошибки и разрешает все имена, которые может разрешить на этой стадии. К этой категории относятся имена, не зависящие от параметров шаблона и разрешаемые стандартным способом или, при необходимости, с помощью аргументов (см. далее). Имена, которые компилятор разрешить не может, называются зависимыми именами, поскольку они так или иначе зависят от параметров шаблона. Разрешение этих имен невозможно до специализации шаблона с фактически переданными аргументами. Следовательно, вторая фаза компиляции шаблона наступает при специализации. Здесь компилятор определяет, нужно ли использовать переопределенную специализацию вместо основного шаблона.

Но прежде чем переходить к примерам, нужно разобраться еще с двумя терминами. Уточненными именами называются имена, построенные по следующим схемам:

класс::имя;

объект.имя;

указатель на объект->имя. Примеры уточненных имен:

MyClass::f():

x.fO:

p->f():

Уточненные имена неоднократно встречались на страницах книги, причем в последний раз при описании ключевого слова typename. Они называются уточненными, потому что целевое имя (как f) явно связывается с конкретным классом или пространством имен. По этой информации компилятор узнает, где искать объявления этих имен.

Другой важный термин - поиск с учетом аргументов (Argument-Dependent Lookup, ADL) Этот механизм изначально разрабатывался для упрощения вызова внешних функций (в том числе операторов), объявленных в пространствах имен. Рассмотрим пример:

linclude <iostream> linclude <string> ...

std: :string sChello ): std::cout s std::endl:

Как обычно в заголовочных файлах, в этом фрагменте отсутствует директива using namespace std. Без этой директивы все имена в пространстве имен std должны



1 ... 62 63 64 [ 65 ] 66 67 68 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика