函数模板和关键字decltype用法的注记

【以下代码均在VS2015环境下调试】

============================================================================

1.

对于一个函数模板,其返回两个变量之和

template
T f(T& a, T&b)
{
return a + b;
}
另外再次定义一个“显式专用化”(explicit specialization)作为函数f的一个例外,其语法格式如下

template<>  type  f    (argument_list)

其中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。
接下来在主函数定义int和double两个类型变量

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(double &,double &)”不是函数模板的专用化 

【目前我也不知道为什么(⊙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类型的数据,尽管此处a、b都是int类型变量。注意如果这个时候没有专用化而是一个同名double型函数:

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*。









你可能感兴趣的:(C++,运算符重载)