Аргументы шаблона для параметров-констант
Параметр шаблона класса может и не быть типом. На аргументы, подставляемые вместо таких параметров, накладываются некоторые ограничения. В следующем примере мы изменяем определение класса 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<24,80> termScreen; termScreen hp2621; |
Screen<8,24> ancientScreen;
Выражение, с которым связан параметр, не являющийся типом, должно быть константным, т.е. вычисляемым во время компиляции. В примере выше typedef termScreen ссылается на экземпляр шаблона Screen<24,80>, где аргумент шаблона для 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; // правильно // ошибка: нельзя вычислить во время компиляции |
Screen< shi, swi > bpObj2; // расширения типа short до int
· преобразования целых типов:
template <unsigned int size> Buf{ ... }; |
(Более подробно они описаны в разделе 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 { ... }; Array<int, 1024U, bar> a0; // правильно: преобразование не нужно Array<int, 1024U, foo> a1; // ошибка: foo != PFV Array<int, 1024, bar> a2; // правильно: 1024 преобразуется в unsigned int Array<int, 1024, bar> a3; // ошибка: foo != PFV Array<int, x, bar> a4; // правильно: преобразование не нужно |
Объекты a0 и a4
класса Array
определены правильно, так как аргументы шаблона точно соответствуют типам параметров. Объект a2
также определен правильно, потому что аргумент 1024
типа int
приводится к типу unsigned int параметра-константы size с помощью преобразования целых типов. Объявления a1, a3 и a5 ошибочны, так как не существует преобразования между любыми двумя типами функций.
Приведение значения 0
целого типа к типу указателя недопустимо:
template <int *ptr> class BufPtr { ... }; // ошибка: 0 имеет тип int // неявное преобразование в нулевой указатель не применяется |
Упражнение 16.3
Укажите, какие из данных конкретизированных шаблонов действительно приводят к конкретизации:
template < class Type > class Stack { }; void f1( Stack< char > ); // (a) class Exercise { // ... Stack< double > &rsd; // (b) Stack< int > si; // (c) }; int main() { Stack< char > *sc; // (d) f1( *sc ); // (e) int iObj = sizeof( Stack< string > ); // (f) |
}
Упражнение 16.4
Какие из следующих конкретизаций шаблонов корректны? Почему?
template < int *ptr > class Ptr ( ... }; template < class Type, int size > class Fixed_Array { ... }; |
(a) const int size = 1024; |
(b) int arr[10]; Ptr< arr > bp2; |
(d) const int hi = 40; const int wi = 80; |
(e) const int size_val = 1024; |
(f) unsigned int fasize = 255; |
(g) const double db = 3.1415; |