《深刻实践C++模板编程》之三——模板参数类型详解

非类型模板参数 和 模板型模板参数
整数以及枚举类型;指向对象或者函数的指针;对对象或函数的引用;指向对象成员的指针。统称为非类型模板参数。
模板型模板参数,是指模板参数还能够是一个模板。
 
一、整数模板参数
非类型模板参数的做用至关于为函数模板或类模板预约义一些常量,在生成模板实例时,也要求必须以常量即编译期已知的值为非类型模板参数赋值。 //就是模板中有一个参数,但它并非模板参数,并不会适配不一样的类型,而是某种固定的类型 那么他的好处在哪?“模板中声明的常量,在模板的全部实例中都具备相同的值,而非类型模板参数则对于在不一样的模板实例中拥有不一样的值来知足不一样的需求”。这句话说得太官方了,咱们来看个例子:
template<typename T>
class CArray { static cosnt unsigned size = 10; T elems[size]; public: T& operator[](unsigned i) throw (std::out_of_range) { if (i >= size) { throw std::out_of_range("Access out of range\n"); } else { return elems[i]; } } };

 

这个例子存在什么问题?问题就在于数组的大小被写死了,这个模板在编译器只能灵活适配不一样的数组类型,可是没法适配不一样的数组大小。可是若是改为这样,就会灵活不少:
 1 template<typename T, unsigned Size>
 2 class CArray2  3 {  4 T elems[Size];  5 public:  6 T& operator[](unsigned i) throw (std::out_of_range)  7 {  8 if (i >= size)  9 { 10 throw std::out_of_range("Access out of range\n"); 11 } 12 else
13 { 14 return elems[i]; 15 } 16 } 17 };

 

让咱们来验证一下,非类型模板参数Size的值不一样,是否产生的是不一样的函数实例,稍微改造一下函数以下:
 
template<typename T, unsigned Size>
class CArray2 { public: CArray2() { id++; } ~CArray2(){} T elems[Size]; public: T& operator[](unsigned i) throw (std::out_of_range) { if (i >= size) { throw std::out_of_range("Access out of range\n"); } else { return elems[i]; } } public: static int id; }; template<typename T, unsigned Size> int CArray2<T, Size>::id = 0; //顺便咱们也应该了解这种带有非类型模板参数的模板类如何定义一个static成员
 
void main() { CArray2<char, 20> array0; printf("ID:%d\n", array0.id); CArray2<char, 20> array1; printf("ID:%d\n", array1.id); CArray2<int, 10> array3; printf("ID:%d\n", array3.id); CArray2<int, 20> array4; printf("ID:%d\n", array4.id); getchar(); } 

 

运行结果以下:

 

 

 
二、函数指针模板参数
前面一小节,在模板参数中固定写死了某种数据类型,这里咱们也能够在定义模板时固定写死某种函数参数类型。而这个函数参数类型又能够适配模板中的模板参数类型。
 
 1 template<typename T, void(*f)(T &v)>
 2 void foreach(T array[], unsigned size)  3 {  4 for (unsigned i = 0; i < size; ++i)  5 {  6 f(array[i]);  7 }  8 }  9  
10 template<typename T>
11 void inc(T &v){ ++v; } 12  
13 template<typename T>
14 void dec(T &v){ --v; } 15  
16 template<typename T>
17 void print(T &v){ printf("%d ", v); } 18  
19 void main() 20 { 21 int array[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; 22 foreach<int, print<int>>(array, 8); 23  
24 foreach<int, inc<int>>(array, 8); 25  
26 getchar(); 27 }
 
三、指针及引用模板参数
只有指向全局变量及外部变量及类静态变量的指针及引用才能够做为模板参数。函数的局部变量、类成员变量等均不能做为模板参数。由于模板参数值必须是编译时已知的。
 
 1 template<int* p>
 2 struct wrapper  3 {  4 int get(){ return *p; }  5 void set(int v){ *p = v; }  6  
 7 };  8  
 9 template<int &p>
10 struct wrapper2 11 { 12 int get(){ return p; } 13 void set(int v){ p = v; } 14 }; 15  
16 int global_variable = 0; 17  
18 int main() 19 { 20 wrapper<&global_variable> gwrapper; 21 wrapper2<global_variable> gwrapper2; 22 }

 

有一个明确的结论是,global_variable 决定了gwrapper的类型。即若是我添加一个
int global_variable3 = 0; 和 wrapper<global_variable3> gwrapper3;
那么gwrapper和gwrapper3并不是同一个类型。
以前咱们讲述的是用typename T来区分两个模板实例,可是这里的一个指针、一个整形常量(统称非模板型模板参数)就能够直接区分模板实例:
 1 template<int* p>
 2 struct wrapper  3 {  4 public:  5 wrapper(){ id++; }  6 int get(){ return *p; }  7 void set(int v){ *p = v; }  8 public:  9 static int id; 10 }; 11 template<int* p> int wrapper<p>::id = 0; 12  
13 int global_variable = 0; 14 int global_variable3 = 0; 15  
16 int main() 17 { 18 wrapper<&global_variable> gwrapper; 19 printf("ID:%d\n", gwrapper.id); 20  
21 wrapper<&global_variable> gwrapper4; 22 printf("ID:%d\n", gwrapper4.id); 23  
24 wrapper<&global_variable3> gwrapper3; 25 printf("ID:%d\n", gwrapper3.id); 26  
27 getchar(); 28  
29 }

 

四、成员函数指针模板参数
 
class some_value { int value; public: some_value(int _value) :value(_value){} int add_by(int op){ return value += op; } int sub_by(int op){ return value -= op; } int mul_by(int op){ return value *= op; } }; typedef int (some_value::* some_value_mfp)(int); template<some_value_mfp func>
int call(some_value &value, int op){ return (value.*func)(op); }//*是必要的,不然会认为是在使用value类的成员func
 
void main() { some_value v0(0); printf("%d\n", call<&some_value::add_by>(v0, 1));//&是必要的,不然会认为是调用some_value::add_by可是没给参数
printf("%d\n", call<&some_value::sub_by>(v0, 2)); printf("%d\n", call<&some_value::mul_by>(v0, 3)); getchar(); }
 
 
五、模板型模板参数
首先我有三个模板:
template<typename T>
struct inc { void operator()(T &v) const { ++v; } }; template<typename T>
struct dec { void operator()(T &v) const { --v; } }; template<typename T>
struct print { void operator()(T &v) const { std::cout << ' ' << v; } };
 
这三个模板决定了foreach生成不一样的实例(固然还有foreach自己的第二个模板参数),这里注意只有类模板能够做为模板参数,因此这里只能用class而不能用struct:
template<template<typename TT> class Func, typename T>
void foreach(T array[], unsigned size) { Func<T> func; for (unsigned i = 0; i < size; i++) { func(array[i]); } }

 

在foreach中使用第一个模板 or 在foreach中使用第二个模板 or 在foreach中使用第三个模板?都有可能!因此要在foreach中添加一个模板参数用来决定使用哪一个模板,这就是模板的模板,也就是模板型模板参数。
void main() { int array[] = { 1, 2, 3, 4, 5, 6, 7 }; foreach<print>(array, 7); foreach<inc>(array, 7); foreach<dec>(array, 7); getchar(); }
相关文章
相关标签/搜索