何为泛型编程呢?简单的说就是,我们按照特定语法写代码,然后让编译器去具体实现这些代码。而函数模板,就是让编译器按照调用时的实参自动生成相应的函数版本.
格式如下:
template<typename AnyType>returnType functionName(argument-list){// do something}其中template和typename是关键字。
Tips: 在c++98之前c++是使用class关键字来定义模板的,而在c++98开始使用typename关键字定义模板了。 示例: 现在假设我们想写一个swap函数来交换两个数的值
#include <iostream>#include<cstring>#include"test_h.h"using std::string ;using std::cout;using std::endl;//function declaration template<typename T>void swap(T& a,T& b);int main(int argc, char** argv) { int i1=19; int i2=20; double d1=19.0; double d2=20.0; cout<<i1<<endl; cout<<i2<<endl; cout<<d1<<endl; cout<<d2<<endl; cout<<"after transform"<<endl; swap(i1,i2); swap(d1,d2); cout<<i1<<endl; cout<<i2<<endl; cout<<d1<<endl; cout<<d2<<endl;}//function definition template<typename T>void swap(T& a,T& b){ T temp; temp=a; a=b; b=temp;}编译器会根据swap()中的参数类型自动生成int和double版本的函数。
Node: 记住template在定义和声明中都是不能少的
假设有如下模板
template<typename T>void f(T a,T b){//……}如果a,b是数组的话a=b
是不可以的 如果a,b是结构的话a>b
也是不可以的 所以模板编程有时候你要考虑一下参数的特殊性。这时候我们就要用到显示具体化了
格式如下:
template <>returnTyoe function< Type>(Type, Type)其中template <>为关键字,如果编译器可以根据参数列表推断模板类型那么< Type>中的Type为可选项
示例:
void swap(int a,int b);template <>void swap(double, double);template<typename T>void swap(T, T);上卖弄三种分别为非模板函数,模板的显式具体化,模板函数
如果程序中同时定义了三种函数,会优先调用非模板函数,然后模板的显式具体化,最后模板函数
一开始定义的模板函数swap(T a,T b)它会告诉编译器生成函数的方式,但是还没有生成函数,而当调用swap(i1,i2)和swap(d1,d2)时就会生成相应的模板实例。模板的显式具体化也一样都是不会生成函数实例的,而是出现了具体类型之后再让编译器按照模板具体化创建函数实例。
在以前c++只能通过判断函数参数来实例化函数,这种叫做隐式实例化而现在的c++可以通过显式实例化了,格式如下
template returnType function<Type>(Type,Type);上面的代码告诉编译器创建一个接受Type类型函数的实例,其中template为关键字,必须要写的。 示例:
template void swap<int>(int,int);//函数实例化template<> void swap<int>(int,int);//模板具体化上面的实例化告诉编译器使用swap模板生成一个接受int参数的函数实例 而模板显式具体化,告诉编译器当调用函数时如果参数为int时,就按照模板具体化定义的形式创建函数。
除了上面的显示实例化的手段,我们还可以在程序中使用函数来创建显示实例化,如下
template<typename T>void swap(T a,T b){ T temp; temp=a; a=b; b=temp;}……int i=19;double d=19.0;cout<<swap<double>(i,d)<<endl;//fun(Type)来显示创建函数实例上卖弄的模板函数与swap(i,d)是不匹配的,因为两个参数是不同的,但是通过显式实例化swap(i,d)来用模板 swap(T a,T b)创建double版本的函数,然后int的值会自动转换为double,以便函数使用
当同时有非模板函数,模板函数,模板显示具体化时定义时。会优先调用非模板函数,然后模板的显示具体化,最后模板函数
void f(int); //#1 float f(float,float=3); //#2 void f(char) //#3 char * f(const char *) //#4 char f(const chat *) //#5 template <class T>void f(const T &) //#6 template <class T>void fT *) //#7如果调用 f('B')
上面#3,#5,#6都是原型匹配,但是#3,#5优先于#6,#6优先于#1
所以c++11引入了关键字decltype来解决这个问题
int x;decltype(x) y;//y is intdecltype(x+y) xy;//xy is x+y typexy=x+y;decltype(x+y)xy=x+y;//xy is x+y type上面是三种decltype的简单使用。而对于如下标准格式 decltype(exPRession) var; 编译器会编译一个核对表,核对表的简化版如下 第一步:如果expresssion是一个没有用括号的标识符,则var的类型与该标识符相同,包括const等修饰符
double x=19.0;double y=19.0double &rx=x;const double *pd;decltype (x) w;//w is doubledecltype (rx) u;//u is double &decltype (pd) v ;//v const double *第二步:如果experssion是一个函数调用,var是的类型与函数返回值一样
int f(int);decltype( f(3)) m//m is intNote: 这实际不会调用函数,编译器会根据函数原型判断返回值类型 第三步:如果expression是一个左值,则var为指向类型的引用。这好像意味着前面w应为引用类型,因为x是一个左值,但是这种情况下已经在第一步处理过了。要进入第三步expression不能是未用括号括起来的标识符
double xx=19.0;decltype((xx))r2=xx;//r2 is double &devltype(xx)w=xx//w is double第四步:如果前面条件都不满足,则var的类型与expression类型相同
int j=3;int& k=j;int& n=j;decltype(j+6) i1;//i1 is intdecltype(100L) i2;//i2 is longdecltype(k+n) i3;//i3 is intdecltype (a+b) 不行吗?因为这时候还没有定义a,b,编译器不知道a,b是什么。c++11为auto新增了语法功能来解决这个问题,如下 auto f(int i1,int i2)->double;
auto告诉编译器返回类型在后面写着。他可以和decltype 连用 auto f(int i1,int i2)->decltype(x+y);
这样就完成了返回类型的自动换了。
新闻热点
疑难解答