Параметр шаблона класса может и не быть типом. На аргументы, подставляемые вместо таких параметров, накладываются некоторые ограничения. В следующем примере мы изменяем определение класса Screen (см. главу 13) на шаблон, параметризованный высотой и шириной:
template int hi, int wid
class Screen {
public:
Screen() : _height( hi ), _width( wid ), _cursor ( 0 ),
_screen( hi * wid, '#' )
{ }
// ...
private:
string _screen;
string::size_type _cursor;
short _height;
short _width;
};
typedef Screen termScreen;
termScreen hp2621;
Screen8,24 ancientScreen;
Выражение, с которым связан параметр, не являющийся типом, должно быть константным, т.е. вычисляемым во время компиляции. В примере выше typedef termScreen ссылается на экземпляр шаблона Screen, где аргумент шаблона для hi равен 24, а для wid - 80. В обоих случаях аргумент - это константное выражение.
Однако для шаблона BufPtr конкретизация приводит к ошибке, так как значение указателя, получающееся при вызове оператора new(), становится известно только во время выполнения:
template int *ptr class BufPtr { ... };
// ошибка: аргумент шаблона нельзя вычислить во время компиляции
BufPtr new int[24] bp;
Не является константным выражением и значение неконстантного объекта. Его нельзя использовать в качестве аргумента для параметра-константы шаблона. Однако адрес любого объекта в области видимости пространства имен, в отличие от адреса локального объекта, является константным выражением (даже если спецификатор const отсутствует), поэтому его можно применять в качестве аргумента для параметра-константы. Константным выражением будет и значение оператора sizeof:
template int size Buf { ... };
template int *ptr class BufPtr { ... };
int size_val = 1024;
const int c_size_val = 1024;
Buf 1024 buf0; // правильно
Buf c_size_val buf1; // правильно
Buf sizeof(size_val) buf2; // правильно: sizeof(int)
BufPtr &size_val & bp0; // правильно
// ошибка: нельзя вычислить во время компиляции
Buf
Вот еще один пример, иллюстрирующий использование параметра-константы для представления константного значения в определении шаблона, а также применение его аргумента для задания значения этого параметра:
template class Type, int size
class FixedArray {
public:
FixedArray( Type *ar ) : count( size )
{
for ( int ix = 0; ix size; ++ix )
array[ ix ] = ar[ ix ];
}
private:
Type array[ size ];
int count;
};
int ia[4] = { 0, 1, 2, 3 };
FixedArray
Выражения с одинаковыми значениями считаются эквивалентными аргументами для параметров-констант шаблона. Так, все три экземпляра Screen ссылаются на один и тот же конкретизированный из шаблона класс Screen:
const int width = 24;
const int height = 80;
// все это Screen 24, 80
Screen
Между типом аргумента шаблона и типом параметра-константы допустимы некоторые преобразования. Их множество является подмножеством преобразований, допустимых для аргументов функции:
1. трансформации l-значений, включающие преобразование l-значения в r-значение, массива в указатель и функции в указатель:
template int *ptr class BufPtr { ... };
int array[10];
BufPtr array bpObj; // преобразование массива в указатель
1. преобразования квалификаторов:
template class Ptr { ... };
int iObj;
Ptr &iObj & pObj; // преобразование из int* в const int*
1. расширения типов:
template int hi, int wid class Screen { ... };
const short shi = 40;
const short swi = 132;
Screen shi, swi bpObj2; // расширения типа short до int
1. преобразования целых типов:
template unsigned int size Buf{ ... };
Buf 1024 bpObj; // преобразование из int в unsigned int
(Более подробно они описаны в разделе 9.3.)
Рассмотрим следующие объявления:
extern void foo( char * );
extern void bar( void * );
typedef void (*PFV)( void * );
const unsigned int x = 1024;
template class Type,
unsigned int size,
PFV handler class Array { ... };
Arrayint, 1024U, bar a0; // правильно: преобразование не нужно
Arrayint, 1024U, foo a1; // ошибка: foo != PFV
Arrayint, 1024, bar a2; // правильно: 1024 преобразуется в unsigned int
Arrayint, 1024, bar a3; // ошибка: foo != PFV
Arrayint, x, bar a4; // правильно: преобразование не нужно