面向对象编程所依赖的多态性称为运行时多态性,泛型编程所依赖的多态性称为编译时多态性或参数试多态性。
所以,要运用泛型编程,首先要了解模板的格式,本文分为模板定义时与使用时格式详解。
函数模板
定义
template<typename T> int compare(T&,T&){}
template<typename T> inline T min(const T&,const T&){}// inline说明符放在 形参表之后,返回类型之前。
使用
使用函数模板时,编译器会推断哪些模板实参绑定到模板形参,一旦确定了实际的模板实参,就称它实例化了函数模板的一个实例
int main(){
cout<<compare(0,1)<<endl;//T is int,隐式调用
string s1 = "hi",s2 = "hello";
cout<<compare(s1,s2)<<endl;//T is string
}
当函数的返回类型必须与形参表中所用的所有类型都不相同时,推断不了模板实参的类型。在这种情况下,有必要覆盖模板实参推断机制,并显示指定为模板实参所用的类型或值。
模板形参使用规则:
- 可以给模板形参赋予的唯一含义是区别形参是类型形参还是非类模板
template<class T,size_t N> void array_init(T(&parm)[N]){//size_t为非类型形参
for(size_t i = 0;i!=N;++i){
parm[i] = 0;
}
}//模板非类型形参是模板定义内部的常量值,在需要常量表达式的时候可使用非类型形参,例如指定数组的长度
- 模板形参遵循常规名字屏蔽规则。在全局作用域中声明的对象,函数或类型同名的模板形参会屏蔽全局名字
- 用作模板形参的名字不能再模板内部重用,这还意味着模板形参的名字只能在同一模板形参表中使用一次:
template<class V,class V> V cale(const V&,const &V)//错误,形参表中两个class V
- 关键字typename和class具有相同的含义,可互换使用。但是关键字typename是作为标准c++的组成部分计入到c++中的,因此旧的程序更有可能只用关键字class。
- 如果要在函数模板内部使用类似size_type的类型,必须显式地告诉编译器我们正在使用的名字指的是一个类型。
template<class Parm,class U> Parm fcn(Parm *array,U value){
typename Parm::size_type *p;//通过在成员名字前面加上关键字typename,可以告诉编译器将成员当做类型。
}
如果拿不准是否需要以typename指明一个名字的类型,那么指定它是个好主意,在类型前指定typename没有害处,因此,即使typename是不必要的,也没有关系。
类模板
接口定义
template<class Type> class Queue{
public:
Queue();
Type &front();
const Type &front()const;
void push(const Type&);
void pop();
bool empty() const;
private:
//...
};
使用
与调用函数模板形成对比,实用类模板时,必须为模板形参显示指定实参
Queue<int> qi;
Queue<vector<double>> qc;
Queue<string> qs;
类模板的每次实例化都会产生一个独立的类类型。为int类型实例化的Queue与任意其他Queue类型没有关系,对其他Queue类型的成员也没有特殊的访问权。
用模板定义的类型总是包括模板实参,例如Queue不是类型,而Queue<int>
或Queue<string>
是类型。
模板实参推断
1.多个类型形参的实参必须完全匹配
template<typename T> int compare(T&,T&){}
int main(){
short si;
compare(si,1024);//错误,不能实例化为compare(short,int)
return 0;
}
如果想允许实参的常规转换,则函数必须用两个类型形参来定义
2.类型形参的实参的受限转换
一般而言不会转换实参以匹配已有的实例化,相反,会产生新的实例。但以下两种编译器会执行转换
- const转换:接收const引用或者const指针的函数可以分别用非const对象的引用或指针来调用,无需产生新的实例化。
- 数组或者函数到指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规指针转换。
template<typename T> T fobj(T,T);
template<typename T> T fref(const &T,const&T);
string s1("a value");
const string s2("another value");
fobj(s1,s2);//正确,调用fobj(string,string),无视const
fref(s1,s2);//正确,非const对象s1转换为const引用。
int a[10],b[42];
fobj(a,b);//正确,调用fobj(int*,int*)
fref(a,b);//错误,当形参为引用时,数组不能转换为指针
类型转换的限制只适用于类型为模板形参的那些实参。
3.模板实参推断与函数指针
template<typename T> int compare(const T&,const T&);
int(*pf1)(const int&,const int&) = compare;//pf1的类型是一个指针,指向“接收两个const int&类型形参并返回int值的函数”,指针pf1引用的是将T绑定到int的实例化。
如果不能从函数指针类型确定模板实参,就会出错。
void func(int(*)(const string&,const string&));
void func(int(*)(const int&,const int&));
func(compare);//错误,通过查看func的形参类型不可能确定模板实参的唯一类型。