如果要写一个交换函数,不同类型的话调用不同的交换函数,如果使用重载的话只能解决函数名相同但是会根据参数类型调用不同的函数。即使这样也依旧要写很多不同类型的swap交换函数
函数重载的交换函数
仔细观察会发现除了类型不同其他的函数结构什么的都一样,都是定义中间变量,然后用它作为过渡交换其他的两个变量。因此可以用C++模版的知识,告诉编译器一个模子,让编译器根据不同类型 利用该模子来生成代码。其实本质上还是写了三个函数,只是模版是让编译器帮你写而已。
通俗点讲就是让编译器往这个模具中填充不同的材料(类型),来获得不同材料的产品(即生成具体的代码)
模板分为两个部分,函数模板和类模板
函数模板
函数模板是函数形式的模板,在编译器编译阶段,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用,需要什么,编译器帮助你实例化你要的函数,比如:当用double类型使用函数模板时,编译器根据传过来参数的类型进行推演,讲实参确定为double类型,然后产生一份专门处理double类型的代码。对于别的类型,比如int它也会直接推演成int类型的函数,本质上还是写了函数,只是编译器自动帮你干了。
函数模板格式
template<typename T1, typename T2,......,typename Tn>返回值类型 函数名 ( 参数列表 ){}
用swap举例子就是
#include<iostream>
using namespace std;
template<typename T>
void swap1(T& a, T& b)
{
T c = a;
a = b;
b = c;
}
int main()
{
int a = 3; int b = 7;
swap1(a,b);
cout << a << endl;
cout << b << endl;
}
但是我为什么写模板swap要写成swap1,这是因为编译器把一些常用的函数已经写成了函数,swap就是其中之一,所以你再写一个swap交换模板就会报错
函数模板的实例化
模板参数实例化分为:隐式实例化和显示实例化,隐式实例化就是和上面swap1模板调用一样不告诉编译器要什么类型swap(a,b),让编译器自己根据实参类型去推演。而显式实例化是告诉了编译器要让模板生成什么类型的函数,基本方式为在函数名后的<>中指定模板参数的实际类型
同样依旧是swap1函数显示实例化的样子
如果传的参数是不同的类型的,编译器会怎么去推演呢,比如add模板,我用一个int类型+double类型会怎么样呢
隐式调用模板
从上图可以看出隐式调用模板会直接报错,因为编译器不知道把T推演出int类型还是double类型
解决办法是把其中一个强制转换成另一个一样的类型,比如把double装换成int类型,但是这样一定会发生精度的丢失
#include<iostream>
using namespace std;
template<typename T>
T add(const T& a, const T& b)
{
return a + b;
}
int main()
{
int a = 3; double b = 7.5;
int ret = add(a, (int)b);//把double类型强制转换成int类型
cout << ret<< endl;
}
但是为什么这里函数形参为什么都加上const呢,把const删了会依旧找不到add,会报错
这是因为类型转换实际上也会产生临时变量进行过渡,double类型强转int转换过程会丢弃浮点数的小数部分。这意味着,无论小数点后的数字是什么,都会被直接忽略。这个过程被称为截断。截断后的整数部分被赋值给一个 int
类型的临时变量(如果这是显式转换的结果)。这个临时变量是编译器在内部生成的,用于存储转换后的整数值。
因为是临时变量具有常性,int引用实际上引用的是这个临时变量,所以要加const
第二种方法是模板T类型直接用改成两个,一个传int类型,一个传double类型。也就是将template<typename T>改成template<typename T1,typename T2>; 但是究竟传什么类型回来呢,难点在于我并不知道返回的究竟是T1还是T2,所以返回值直接用auto修饰,让编译器自己去推导就可以解决了
#include<iostream>
using namespace std;
template<typename T1, typename T2>
auto add(const T1& a, const T2& b)
{
return a + b;
}
int main()
{
int a = 3; double b = 7.5;
int ret = add(a, (int)b);
cout << ret<< endl;
}
第三种办法就是显示实例化
总结一下如果有不同的类型的参数调函数模板,无论怎样都要进行强制类型转换,都会造成精度的丢失
模板参数匹配规则
如果既有普通的函数又有函数模板推导并且普通函数参数和调用的地方实参类型对应的上的话,那么编译器会优先调用普通函数(优先匹配普通函数+参数匹配)
如果没有普通的函数或者普通的函数参数和实参类型对应不上的话,会去优先匹配参数类型对应的上的函数模板
总结一下就是先普通函数然后模板函数
类模板格式
template < class T1 , class T2 , ..., class Tn >class 类模板名{// 类内成员定义};
// 动态顺序表// 注意: Vector 不是具体的类,是编译器根据被实例化的类型生成具体类的模具template < class T >class Vector{public :Vector ( size_t capacity = 10 ): _pData ( new T [ capacity ]), _size ( 0 ), _capacity ( capacity ){}// 使用析构函数演示:在类中声明,在类外定义。~Vector ();void PushBack ( const T & data ) ;void PopBack () ;// ...size_t Size () { return _size ;}T & operator []( size_t pos ){assert ( pos < _size );return _pData [ pos ];}private :T * _pData ;size_t _size ;size_t _capacity ;};// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表template < class T >Vector < T > :: ~Vector (){if ( _pData )delete [] _pData ;_size = _capacity = 0 ;}
通俗点类模板只是使类成员成为模板类型而已,可一个也可以所有都成为模板类型。
类模板实例化
// Vector 类名, Vector<int> 才是类型Vector < int > s1 ;Vector < double > s2 ;