函数模板的声明
template <typename T>//typename 也可以用class替换
void Swap(T &a,T &b)
{
T tmp=a;
a=b;
b=tmp;
}
int main()
{
int a=3,b=4;
Swap(a,b);
cout<<a<<' '<<b<<endl;
}
有时编写的模板函数很可能无法处理某些类型,这时候我们可以为特定类型提供具体化的模板定义。
显示具体化
- 对于给定的函数名,可以有非模板函数,模板函数和显示具体化模板函数以及它们的重载版本。
- 显示具体化的原型和定义应以template<>打头,并通过名称来指出类型。
- 非模板版本优先于显示具体化,显示具体化优先于模板版本(非模板版本>显示具体化>模板版本)。
下面是用于交换job结构的非模板函数,模板函数和具体化的原型
struct job
{
char name[40];
double salary;
int floor;
};
//非模板函数
void Swap(job &,job &);
//模板函数
template <typename T>
void Swap(T &,T &);
//显示具体化
template<> void Swap<job>(job &,job &);
实例化和具体化的区别
- 在代码中包含函数模板并不会生成函数定义,它只是一个用于生成函数定义的方案。
- 在调用模板函数时,编译器自动生成函数定义,得到模板实例 – 隐式实例化。
- 显式实例化 – 前加template关键字 (不加尖括号)
template void swap_diy<char>(char&, char&); // 只需要声明即可
- 此声明令编译器使用模板生成一个使用char类型的函数定义 – 即生成模板实例
- 不能在同一个文件中同时使用同一种类型的显式具体化和显式实例化
- 也可在函数调用时显式指定类型
swap_diy<double>(a, b);
重载解析
步骤
-
创建候选函数列表。其中包含与被调用函数的名称相同的函数和模板函数。
-
使用候选函数列表创建可行函数列表。这些都是参数数目正确的函数,为此有一个隐式转换序列,其中包括实参类型与相应的形参类型完全匹配的情况。
-
确定是否有最佳的可行函数。
- 完全匹配,但常规函数优先于模板。
- 提升转换(如,char和shorts自动转换为int,float自动转换为double)。
- 标准转换(例如,int转换为char,long转换为double)。
- 用户定义的转换,如类声明中定义的转换。
如果出现多个完全匹配的函数,编译器将会生成一条错误消息-“二义性”。但有时候,即使两个函数都完全匹配,仍可完成重载解析。首先指向非const数据的指针和引用优先于const指针和引用参数匹配。(注意只适用于指针和引用)。
另一个完全匹配优于另一个的情况是,其中一个是非模板函数,而另一个不是。这种情况下,非模板函数将优先于模板函数(包括显示具体化)。
如果两个完全匹配的函数都是模板函数,则较具体的模板函数优先。这意味着显示具体化将优于使用模板隐式生成的具体化。
关键字decltype(C++11)
-
为了解决某些类型是由模板(或其组合)决定,而难以表示的问题
template<typename T1, typename T2> void fun(T1& a, T2& b) { some_type apb = a + b; }
-
decltypevar 使变量var与expression的类型一样。
template<typename T1, typename T2> void fun(T1& a, T2& b) { decltype(a+b) apb = a + b; }
-
如果expression是一个没有用括号括起来的标识符,则var的类型与该标识符类型相同,包括const等限定符:
const int* x; decltype(x) w; // w的类型为const int*
-
如果expression是一个函数调用,则var的类型与函数的返回类型相同。
long fun(int); decltype(fun(3)) m; // m的类型为long
-
如果expression是一个左值,则var为指向其类型的引用(必须加括号)
double x = 3.3; decltype((x)) r = x; // r为double&类型 decltype(x) v = x; // v为double类型
-
如果前面的条件都不满足,则var的类型与expression的类型相同。
-
若需要多次声明此类型,可结合typedef
template<class T1, class T2> void fun(T1 x, T2 y) { typedef decltype(x + y) xpytype; // 将其定义为另一种类型名 xpytype xpy = x + y; xpytype arr[10]; xpytype& rxy = arr[2]; }
后置返回类型(C++11)
template<class T1,class T2>
some_type gt(T1 x,T2 y)
{
return x+y;
}
-
此时还没有声明x和y,所以不能用decltype。
-
对于下面的原型
double h(int x,float y); //可以写成 auto h(int x,float y)->double;
-
这将返回类型移到参数声明后面。->double被称为后置返回类型。其中auto是一个占位符,表示后置返回类型提供的类型。所以上面的模板函数可写成
template<class T1,class T2> auto gt(T1 x,T2 y)->decltype(x+y) { return x+y; }