Выбрать главу

// ...

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 ) вне зависимости от того, какую из точек конкретизации выберет компилятор.

Упражнение 10.13

Назовите два шага разрешения имени в определениях шаблона. Объясните, каким образом первый шаг отвечает потребностям разработчика библиотеки, а второй обеспечивает гибкость, необходимую пользователям шаблонов.

Упражнение 10.14

На какие объявления ссылаются имена 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 {

// определение шаблона скрыто в пространстве имен