模板参数
模板参数分为类型参数和非类型参数。类型参数代表的是一个基本类型或用户自定义的类型,而非类型参数代表一个常量。
每个参数前面都必须有关键字typename或者class,形式为<typename typeparameter>或<class typeparameter>。
一个模板的非类型参数为文字常量的例子:
#include <iostream> using namespace std; //T 是类型参数, Size是非类型参数 template <typename T, int Size> class Average { T elements[Size]; T average_value; public: Average(T *in) { average_value = (T)0; for(int i = 0; i < Size; i++) { elements[i] = in[i]; average_value += in[i]; } average_value /= Size; } void show() { cout<<"The elements are: "; for(int i = 0; i < Size; i++) { cout<<elements[i]<<" "; } cout<<endl<<"The average is: "<<average_value<<endl; } }; int main() { int arr_int[3] = {1, 2, 3}; double arr_dou[4] = {1.4, 9.8, 3.4, 0.9}; Average<int, 3> ia(arr_int); Average<double, 4> da(arr_dou); ia.show(); da.show(); return 0; }
上面的例子中,非类型参数是一个实例化的文字常量,但是,是不是模板的非类型参数只能是实例化的文字常量呢???答案
是:它也可以被实例化为在编译阶段能够静态计算的表达式。看下面的例子:
一个模板的类型参数为全局变量的例子:
#include <iostream> using namespace std; template <typename T, T *Total, T *Number> class Single { T single_value; public: Single(T s) { single_value = s; (*Total) += s; (*Number)++; } }; //定义几个全局变量, 并不是实例化的文字常量 int Int_Total; int Int_Number; double Dou_Total; double Dou_Number; int main() { //将全局变量 Int_Total, Int_Number的地址传给模板参数 Single<int, &Int_Total, &Int_Number> arr_int[3] = {1, 2, 3}; //将全局变量 Dou_Total, Dou_Number的地址传给模板参数 Single<double, &Dou_Total, &Dou_Number> arr_dou[4] = {1.4, 2.3, 3.9, 4.0}; cout<<"The number of integers is: "<<Int_Number<<endl; cout<<"The total value of integers is: "<<Int_Total<<endl; cout<<"The number of doubles is: "<<Dou_Number<<endl; cout<<"The total value of doubles is: "<<Dou_Total<<endl; return 0; }
实际应用中,我们常用基本数据类型或者自定义类型来实例化模板的类型参数。那么,是不是可以用一个模板类来实例化某个模板的类型参数呢? 当然是可以的。比如像这样的:Array<Array<int, 3>, 4> 看下面一段程序:
#include <iostream> using namespace std; //定义一个模板类 template <typename T, int Length> class Array { public: int size; T elements[Length]; public: int GetSize() { return size; } Array():size(Length){} //size = Length //重载操作符 [] T& operator[](int i) { return elements[i]; } //友元函数, 重载了 操作符<< template <typename T> friend ostream operator<<(ostream & out, const Array<T, Length> &); }; //友元函数 //template <typename T> //这样的写法是错误的 template <typename T, int Length> //不能少,必须和类模板一致 ostream& operator<<(ostream& out, const Array<T, Length> &a) { for(int i = 0; i < a.size; i++) { out<<a.elements[i]; //判断模板参数的类型是否为int或者double if(typeid(a.elements[i]) == typeid(int) || typeid(a.elements[i]) == typeid(double)) out<<", "; } out<<"\b\b "; return out; } int main() { Array<double, 3> a; a[0] = 1.1; //因为重载了操作符[],所以这句相当于 elements[0] = 1.1 a[1] = 2.2; a[2] = 3.3; cout<<a<<endl; //重载了操作符<< //将Array<int, 3>作为一个模板参数 Array<Array<int, 3>, 4> a2; //a2为一个二维数组,a2[4][3] int k = 0; for(int i = 0; i < a2.GetSize(); i++) for(int j = 0; j < a2[i].GetSize(); j++) //a2[i].GetSize() = 3 a2[i][j] = ++k; cout<<a2<<endl; return 0; }
使用友元函数的时候,如果友元本身是一个函数模板,则应在friend前面加template关键字,防止编译器将operator<<()当做一个普通的函数看待。
>>关于模板类,还有一个更重要的应用,那就是模板的某个类型参数,其本身是另一个类模板,将它显式地写在模板参数列表
中。 例如定义一个模板 template <class T, int a, template<class T, int a>class A> ,参数列表中的template<class T, int
a>class A本身就是一个类模板。具体看下面的例子:
#include <iostream> using namespace std; //定义一个模板类 Array template <class T, int a> class Array { int size; public: T val[a]; Array():size(a){} //重载了 [] T & operator[](int i) { return val[i]; } void show() { cout<<"The elements of the array is: "; for(int i = 0; i < size; i++) cout<<val[i]<<" "; cout<<endl; } }; //定义一个模板类 Student template<class T, int a> class Student { int age; public: double score; Student():age(a){} void show() { cout<<"The student's age is:"<<age<<" and its score is:"<<score<<endl; } }; //定义模板类Container, 可以将另一个模板类作为参数 template <class T, int a, template<class T, int a>class A> class Container { public: A<T, a> entity; void show() { entity.show(); } }; int main() { //将模板类 Array 作为一个参数 Container<double, 3, Array> obj; obj.entity[0] = 1.6; obj.entity[1] = 3.4; obj.entity[2] = 2.0; obj.show(); //将模板类 Student 作为一个参数 Container<double, 18, Student> stu; stu.entity.score = 99; stu.show(); return 0; }
一般的类模板在实例化的时候,它的类型参数和非类型参数将被替换,而类名本身不发生变化。而模板的模板参数在实例化的
时候,不但其类型参数和非类型参数要被替换,类型也必须被替换。例如,在上面的程序中,A<T, a>就被实例化为
Array<double, 3>和Student<int, 18>。如果在一个类模板的内部要用到另一个类模板,但在实例化的时候,另一个类模板的名
字是不变的,那么就没有必要使用模板的模板参数,只需要在类模板的内部直接使用另一个类模板就可以了。