В действительности точка конкретизации находится после определения каждой функции, в которой используется конкретизированный экземпляр. Компилятор может выбрать любую из этих точек, чтобы конкретизировать в ней шаблон. Отсюда следует, что при организации кода программы надо быть внимательным и помещать все объявления, необходимые для разрешения имен, зависящих от параметров некоторого шаблона, перед первой точкой. Поэтому разумно поместить их в заголовочный файл, который включается перед любой возможной конкретизацией шаблона:
#include primer.h
// user.h содержит объявления, необходимые при конкретизации
#include "user.h"
void another();
SmallInt asi[4];
int main() {
// ...
}
// первая точка конкретизации min(SmallInt*,int)
void another() {
// ...
}
// вторая точка конкретизации min(SmallInt*,int)
А если конкретизация шаблона происходит в нескольких файлах? Например, что будет, если функция another() находится в другом файле, нежели main()? Тогда точка конкретизации есть в каждом файле, где используется конкретизированная из шаблона функция. Компилятор свободен в выборе любой из них, так что нам снова придется проявить аккуратность и включить файл "user.h" во все исходные файлы, где используются конкретизированные функции. Тем самым гарантируется, что реализация min(SmallInt*,int) будет ссылаться именно на нашу функцию print(const SmallInt ) вне зависимости от того, какую из точек конкретизации выберет компилятор.
Назовите два шага разрешения имени в определениях шаблона. Объясните, каким образом первый шаг отвечает потребностям разработчика библиотеки, а второй обеспечивает гибкость, необходимую пользователям шаблонов.
На какие объявления ссылаются имена display и SIZE в реализации max(LongDouble*,SIZE)?
// ---- exercise.h ----
void display( const void* );
typedef unsigned int SIZE;
template typename Type
Type max( Type* array, SIZE size )
{
Type max_val = array[0];
for ( SIZE i = 1; i size; ++i )
if ( array[i] max_val )
max_val = array[i];
display( "Maximum value found: " );
display( max_val );
return max_val;
}
// ---- user.h ----
class LongDouble { /* ... */ };
void display( const LongDouble );
void display( const char * );
typedef int SIZE;
// ---- user.C ----
#include exercize.h
#include "user.h"
LongDouble ad[7];
int main() {
// задать значения элементов массива ad
// конкретизируется max( LongDouble*, SIZE )
SIZE size = sizeof(ad) / sizeof(LongDouble);
max( ad[0], size );
}
10.10. Пространства имен и шаблоны функций А
Как и любое другое глобальное определение, шаблон функции может быть помещен в пространство имен (см. обсуждение пространств имен в разделах 8.5 и 8.6). Мы получили бы ту же семантику, если бы определили шаблон в глобальной области видимости, скрыв его имя внутри пространства имен. При использовании вне этого пространства необходимо либо квалифицировать имя шаблона именем пространства имен, либо использовать using-объявление:
// ---- primer.h ----
namespace cplusplus_primer {
// определение шаблона скрыто в пространстве имен
template class Type
Type min( Type* array, int size ) { /* ... */ }
}
// ---- user.C ----
#include primer.h
int ai[4] = { 12, 8, 73, 45 };
int main() {
int size = sizeof(ai) / sizeof(ai[0]);
// ошибка: функция min() не найдена
min( ai[0], size );
using cplusplus_primer::min; // using-объявление
// правильно: относится к min() в пространстве имен cplusplus_primer
min( ai[0], size );
}