// ...
int main() {
// ...
// использование min(SmallInt*,int)
min( asi[0], size );
}
// точка конкретизации min(SmallInt*,int)
// как будто объявление конкретизированной функции выглядит так:
SmallInt min( SmallInt* array, int size )
{ /* ... */ }
Но что, если конкретизация шаблона случается в одном исходном файле несколько раз? Где тогда будет точка конкретизации? Вы можете спросить: “А какая, собственно, разница?” В нашем примере для SmallInt разница есть, поскольку объявление функции print(const SmallInt ) должно появиться перед точкой конкретизации min(SmallInt*,int):
#include primer.h
void another();
SmallInt asi[4];
int main() {
// задать значения элементов массива asi
int size = sizeof(asi) / sizeof(SmallInt);
min( asi[0], size );
another();
// ...
}
// точка конкретизации здесь?
void another() {
int size = sizeof(asi) / sizeof(SmallInt);
min( asi[0], size );
}
// или здесь?
В действительности точка конкретизации находится после определения каждой функции, в которой используется конкретизированный экземпляр. Компилятор может выбрать любую из этих точек, чтобы конкретизировать в ней шаблон. Отсюда следует, что при организации кода программы надо быть внимательным и помещать все объявления, необходимые для разрешения имен, зависящих от параметров некоторого шаблона, перед первой точкой. Поэтому разумно поместить их в заголовочный файл, который включается перед любой возможной конкретизацией шаблона:
#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 {
// определение шаблона скрыто в пространстве имен