【以下代码均在VS2015环境下调试】
============================================================================
1.
对于一个函数模板,其返回两个变量之和
template
T f(T& a, T&b)
{
return a + b;
}
另外再次定义一个“显式专用化”(explicit specialization)作为函数f的一个例外,其语法格式如下
template<> type f
其中f之后的
template<> type f (argument_list)
例如返回两个参数的差,可以有
template<>double f(double&a, double&b)
{
return a - b;
}
或者
template<>double f(double&a, double&b)
{
return a - b;
}
特别注意以下是
错误的
template<>T f(double&a, double&b)
{
return a - b;
}
不能指定一个明确类型(double)后还使用模板类型T。
void main()
{
using std::cout;
using std::endl;
int a = 1, b = 2;
double c = 2.25, d = 3.25;
cout << f(a, b) << endl;
cout << f(c, d) << endl;
}
其输出结果是,f(a,b)=3,,f(c,d)=-1。可见当两个参数是int(或者其他不是double的变量都将调用模板),而遇到两个double变量c、d时将调用显式专用化函数返回二者之差。
然而,如果返回类型不是空void时候,可以使用auto+decltype(C++11支持)关键字来指定返回类型(尾随返回类型)
template<> auto f(double&a, double&b)->decltype(a+b)
{
return a - b;
}
也就是说它返回a+b的类型。也就是延迟指定返回类型(trailing return type)
template
T f(T& a, T&b)
{
return a + b;
}
template<> auto f(double&a, double&b)->decltype(a+b)
{
return a - b;
}
但是注意,两个函数
不能同时声明auto+decltype
template
auto f(T& a, T&b)->decltype(a + b)
{
return a + b;
}
template<> auto f(double&a, double&b)->decltype(a+b)
{
return a - b;
}
或者
template
auto f(T& a, T&b)->decltype(a + b)
{
return a + b;
}
template<> double f(double&a, double&b)
{
return a - b;
}
均提示
C2912 显式专用化;“double f
【目前我也不知道为什么(⊙o⊙)…】
但是考虑到,如果同名的非模板函数和模板函数同时存在的话,则非模板函数会覆盖(override)模板函数,由此可以
template
auto f(T a, T b)->decltype(a + b)
{
return a + b;
}
auto f(double a, double b)->decltype(a + b)//或auto f(double a, double b)->double
{
return a - b;
}
此时如果主函数
void main()
{
using std::cout;
using std::endl;
int a = 1, b = 2;
double c = 2.25, d = 3.25;
cout << f(a, b) << endl;
cout << f(c, d) << endl;
}
仍然显示f(a,b)=3,,f(c,d)=-1。这就说,尽管int可以转为double,但是如果两个类型相同变量均不是double,此时模板函数是最佳匹配。
进一步,如果至少一个是double,那么就会模板函数就不是最佳匹配,会调用非模板函数
void main()
{
using std::cout;
using std::endl;
int a = 1, b = 2;
double c = 2.25, d = 3.25;
cout << f(double(a), b) << endl;
cout << f(c, d) << endl;
}
则显示-1,-1。
另外,如果两个变量类型不同,则会自动转化为double调用非模板函数而不是模板函数,因为模板函数保证两个参数的类型必须一致,而此处不一致。
void main()
{
using std::cout;
using std::endl;
int a = 1, b = 2;
double c = 2.25, d = 3.25;
cout << f(float(a), short(b)) << endl;
cout << f(c, d) << endl;
}
则显示-1,-1。
如果我们有
template
T f(T a, T b)//primary template
{
return a + b;
}
template<>double f(double a, double b)//专用化
{
return a - b;
}
那么若主函数中有
void main()
{
using std::cout;
using std::endl;
int a = 1, b = 2;
cout << f(float(a), short(b)) << endl;
}
其会提示错误
没有与参数列表匹配的 函数模板 "f" 实例(cout << f(float(a), short(b)) << endl; )
C2782“unknown-type f(T,T)”: 模板 参数“T”不明确(cout << f(float(a), short(b)) << endl; )
由此可见,专用化的形式上仍然和模板函数一致,但是非模板函数则没有限制。
C++允许:同名的:非模板函数、模板函数、模板专用化函数以及其各自的重载函数同时存在。如果三类型均匹配当前调用,则就优先级而言,非模板函数先于模板专用化函数,模板专用化函数先于模板函数。
2.
模板实例化(instantiaion)和模板专用化(specialization)
模板专用化第一部分已经提及,它相当于,对于该模板,对于某些特别类型的需要特殊算法,则可以声明一个专用化。然而注意到,一个函数模板只是一个框架,实际上在没有调用之前它什么也不是,只用调用时才会产生相应类型的函数代码。此时我们成为模板实例化。其实之前已经接触到了实例化例子,只不过是隐式实例化
int a=1,b=2;
cout<
也就是说,f(a,b)此处调用了模板来实例化了一个针对int类型的函数代码。
那么当然也可以有显示实例化,声明如下:
template
T f(T a, T b)
{
return a + b;
}
template int f(int, int);//模板实例化
特别注意和专用化的语法格式区别
template<> int f(int, int);//模板实例化
可见
实例化没有尖括号。
void main()
{
using namespace std;
template int f(int, int);
C2951 模板 声明只能在全局、命名空间或类范围内使用
当前范围内不允许显式实例化
C2252 只能在命名空间范围内显式实例化模板
注意到如果声明了一条实例化语句,则相当于产生了确定的函数代码。
当然在代码块内显式声明实例化,则要类似于:
cout << f(a,b)<< endl;
这就声明了一个int类型的函数实例。
接下来如果将其声明为double类型的函数实例代码:
cout << f(a,b)<< endl;
template<>double f(double a, double b)
{
return a - b;
}
double f(double a, double b)
{
return a - b;
}
cout << f(a,b)<< endl;
是不会调用非模板函数,因为非模板函数不属于模板,而此处显然是一个模板实例化声明。
实例化声明语句可以有很多条。
3.
选择哪一个函数?
如果有两个模板,是重载的函数:
template
void f(T a, T b)//@1
{
cout<<"general"<
void f(T* a, T* b)//@2
{
cout << "pointer" << endl
}
则如果参数是一个int指针int*,选择哪一个?是选@1,使得T变成int*,还是@2,使得T变成int?尽管两个都解释的同,此时编译器选择明确指示的一个,因为@2的参数列表已经指明了它需要两个T型指针,因此如果传入两个int指针,那么会显示“pointer”而不是“general”。
注意到,如果仅仅只有
template
void f(T a, T b)
{
cout<<"general"<
那么传入两个int'*指针,则能且只能选择f,把T解释为int*。