泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。
模板有函数模板和类模板。这篇文章讲函数模板。
函数模板例子。
template<typename T>
void Swap( T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
下面用代码实现一下。
#include<iostream> //template <class T> using namespace std; namespace bit { template<typename T> void swap(T& left, T& right) { T tmp = left; left = right; right = tmp; } } int main() { int a = 1, b = 2; swap(a, b); cout << a << ' ' << b << endl; char c = 'b', d = 'c'; swap(a, b); cout << c << ' ' << d << endl; }
结果。
只写一个函数,但两种类型都能用。
他们两个其实调用了两个不同的函数,两个函数栈帧的大小也不一样。我们实际调用的是模具生成的对应的函数。
模板是写给编译器的,编译器会推演模板,生成对应的函数,生成函数的过程叫函数模板的实例化。
如果如下调用,你们函数模板只会实例化一次。
但是像下面这样swap里面有两个类型,在上面模板下,编译器就会报错。
而且它不会强转,因为强转和转换只会在传参或赋值时发生。而这个报错是在传参或赋值前,传参和赋值是在实际调用时,而这里面在实际调用前编译器通过参数推出对应的模板,是在模板推演阶段报错的。
而且就算它到了强转那步也会有问题,因为模板形参是引用,而转化后的临时对象具有常性,是不能变的,相当于const T型,是不能传给T形的,因为那样此对象权限会扩大的,而也不能在形参定义时T&前加const,因为那样函数内就交换不了了。
但是,我们可以选择在推演之前进行强转。
#include<iostream>
using namespace std;
template<class T>
template<typename T>
T Add( const T& left,const T& right)
{
return left + right;
}
int main()
{
int a = 1, b = 2;
double e = 1.1;
cout <<Add(a, (int)e) << endl;
}
但是只要去掉Add后面的const去掉,Add就会报错,因为强转后,模板通过强转后的那个隐式类型进行推演,而隐式类型具有常性,不加const相当于权限放大,此模板就不会匹配,推演也就不能完成。
#include<iostream>
using namespace std;
template<class T>
template<typename T>
T Add( const T& left,T& right)
{
return left + right;
}
int main()
{
int a = 1, b = 2;
double e = 1.1;
cout <<Add(a, (int)e) << endl;
}
我们也可以显式实例化。
#include<iostream>
using namespace std;
template<class T>
template<typename T>
T Add( const T& left, const T& right)
{
return left + right;
}
int main()
{
int a = 1, b = 2;
double e = 1.1;
cout <<Add<int>(a, e) << endl;
}
但不加const,也会报错。
因为此时,显示实例化,相当于把T全部换为int,而e要传参就要隐式类型转换,因为隐式类型转换,临时变量具有常性,所以没有const,相当于权限放大,就会报错。
下面这样写也不行,因为e是强制类型转换的,同样具有常性,第二个T前也需要加const。
#include<iostream>
using namespace std;
template<class T>
void Swap(T& left, T& right)
{
T tmp = left;
left = right;
right = tmp;
}
template<class T>
T Add( T& left, T& right)
{
return left + right;
}
int main()
{
int a = 1, b = 2;
double e = 1.1;
cout <<Add<int>(a, (int)e) << endl;
}
下面看下双变量模板。
#include<iostream>
using namespace std;
template<class T>
template<typename T1, typename T2>
T1 Add( T1& left,const T2& right)
{
return left + right;
}
int main()
{
int a = 1, b = 2;
double e = 1.1;
cout <<Add(a, e) << endl;
}
因为返回值是T1类型,所以返回值是整形
返回值改成T2返回值就变成浮点型了。
注意不能同时定义两个相同模板,但不同返回值类型的。
两个参数也可以显示实例化,但下面这样只会显示实例化第一个参数。
#include<iostream>
using namespace std;
template<class T>
template<typename T1, typename T2>
T1 Add(const T1& left, const T2& right)
{
return left + right;
}
int main()
{
int a = 1, b = 2;
double e = 1.1;
cout <<Add<double>(a, e) << endl;
}
下面这么写并没有报错,说明两个参数的模板,Add<int>只会对第一个参数实例化为int型,因为如果对第二个参数也实例化了,那么e会隐式类型转换,T2前没有const,e的参数权限放大,会报错。
下图可以证明上面结论
模板和函数能同时存在。 说明函数和模板内部实现是有区别的。