Но зачем нужны два шага? Почему бы, например, не разрешать все имена при конкретизации?
Если вы проектируете шаблон функции, то, вероятно, хотели бы сохранить контроль над тем, когда разрешаются имена в его определении. Предположим, что шаблон 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 );
}
Объявление функции print( const char* ) в файле user.C невидимо в том месте, где появляется определение шаблона. Однако оно видимо там, где конкретизируется шаблон min(int*,int), но это объявление не рассматривается при компиляции вызова print("Minimum value found: "), так как последний не зависит от параметров шаблона. Если некоторая конструкция в определении шаблона не зависит от его параметров, то имена разрешаются в контексте самого определения, и результат разрешения в дальнейшем не пересматривается. Поэтому на программиста возлагается ответственность за то, чтобы объявления имен, встречающихся в определении, были включены в интерфейс библиотеки вместе с шаблоном.
А теперь предположим, что библиотека была написана кем-то другим, а мы ее пользователи, которым доступен интерфейс, определенный в заголовочном файле primer.h. Иногда нужно, чтобы объекты и функции, определенные в нашей программе, учитывались при конкретизации шаблона из библиотеки. Допустим, мы определили в своей программе класс SmallInt и хотели бы конкретизировать функцию min() из библиотеки primer.h для получения минимального значения в массиве объектов типа SmallInt.
При конкретизации шаблона min() для массива объектов типа SmallInt вместо аргумента шаблона Type подставляется тип SmallInt. Следовательно, min_val в конкретизированной функции min() имеет тот же тип. Тогда как разрешится вызов функции print(min_val)?
// ---- user.h ----
class SmallInt { /* ... */ }
void print( const SmallInt );
// ---- user.C ----
#include primer.h
#include "user.h"
SmallInt asi[4];
int main() {
// задать значения элементов массива asi
// конкретизируется min( SmallInt*, int )
// int size = sizeof(asi) / sizeof(SmallInt);
min( asi[0], size );
}
Это нормально: мы хотим, чтобы учитывалась именно наша функция print(const SmallInt ). Рассмотрения функций, определенных в библиотеке primer.h, недостаточно. Второй шаг разрешения имени гарантирует, что если имя, использованное в определении, зависит от параметров шаблона, то принимаются во внимание имена, объявленные в контексте конкретизации. Поэтому можно быть уверенным, что функции, умеющие манипулировать объектами типа SmallInt, попадут в поле зрения компилятора при анализе шаблона, которому в качестве аргумента передан тип SmallInt.
Место в программе, где происходит конкретизация шаблона, называется точкой конкретизации. Знание этой точки важно потому, что она определяет, какие объявления учитывает компилятор для имен, зависящих от параметров шаблона. Такая точка всегда находится в области видимости пространства имен и следует за функцией, внутри которой произошла конкретизация. Например, точка конкретизации min(SmallInt*,int) расположена сразу после функции main() в области видимости пространства имен: