// ---- primer.h ----

// это объявление необходимо:

// внутри min() вызывается print( const char * )

void print( const char * );

template typename Type

Type min( Type* array, int size ) {

// ...

print( "Minimum value found: ");

print( min_val );

return min_val;

}

С другой стороны, объявление функции print(), используемой для печати min_val, пока не нужно, так как еще неизвестно, какую конкретно функцию надо искать. Мы не знаем, какая функция print() будет вызвана при обращении print(min_val), пока тип min_val не станет известным.

Когда же должна быть объявлена функция print(), вызываемая при обращении print(min_val)? До конкретизации шаблона. Например:

#include primer.h

void print( int );

int ai[4] = {12, 8, 73, 45 };

int main() {

int size = sizeof(ai) / sizeof(int);

// конкретизируется min( int*, int )

min( ai[0], size );

}

main() вызывает конкретизированную из шаблона функцию min(int*,int). В этой реализации Type заменено int, и тип переменной min_val, следовательно, равен int. Поэтому при обращении print(min_val) вызывается функция с аргументом типа int. Именно тогда, когда конкретизируется min(int*,int), становится известно, что при втором вызове аргумент print() имеет тип int. В этот момент такая функция должна быть видима. Если бы функция print(int) не была объявлена до конкретизации min(int*,int), то компилятор выдал бы сообщение об ошибке.

Поэтому разрешение имен в определении шаблона происходит в два этапа. Сначала разрешаются имена, не зависящие от его параметров, а затем, при конкретизации, – имена, зависящие от параметров.

Но зачем нужны два шага? Почему бы, например, не разрешать все имена при конкретизации?

Если вы проектируете шаблон функции, то, вероятно, хотели бы сохранить контроль над тем, когда разрешаются имена в его определении. Предположим, что шаблон min() – это часть библиотеки, в которой определены и другие шаблоны и функции. Желательно, чтобы реализации min() по возможности использовали другие компоненты нашей же библиотеки. В предыдущем примере интерфейс библиотеки определен в заголовочном файле primer.h. Как объявление функции print(const char*), так и определение функции min() являются частями интерфейса. Мы хотим, чтобы конкретизации шаблона min() пользовались функцией print() из нашей библиотеки. Первый этап разрешения имени это гарантирует. Если имя, использованное в определении шаблона, не зависит от его параметров, то оно обязательно будет относиться к компоненту внутри библиотеки, т.е. к тому объявлению, которое включено в один пакет с этим определением в заголовочном файле primer.h.

На самом деле автор шаблона должен позаботиться о том, чтобы были объявлены все имена, использованные в определениях и не зависящие от параметров. Если этого нет, то определение шаблона вызовет ошибку. При конкретизации шаблона компилятор ее не исправляет:

// ---- primer.h ----

template typename Type

Type min( Type* array, int size )

{

Type min_val = array[0];

// ...

// ошибка: функция print( const char* ) не найдена

print( "Minimum value found: " );

// правильно: зависит от параметра шаблона

print( min_val );

// ...

}

// ---- user.C ----

#include primer.h

// это объявление print( const char* ) игнорируется

void print( const char* );

void print( int );

int ai[4] = {12, 8, 73, 45 };

int main() {

int size = sizeof(ai) / sizeof(int);

// конкретизируется min( int*, int )

min( ai[0], size );

}

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

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