函数模板和模板函数
定义:
函数模板可以用来创建通用的函数(该函数被称为模板函数),支持任意类型的形参和返回值,避免重载函数的函数体重复设计;
函数模板格式:
template<class 形参1, class 形参2, ... , class 形参n>
返回类型 函数名( 参数表 )
{
...
}
模板形参的定义既可以使用class,也可以使用typename,二者含义相同;
例:
#include<iostream>
#include<string>
using namespace std;
template<class T>
bool IsEqual(const T& left, const T& right)
{
return left == right;//判断是否相等
}
void test()
{
int num1 = 1, num2 = 1;
string s1("abc"), s2("bcd");
cout<<IsEqual(num1,num2)<<endl;
cout<<IsEqual(s1,s2)<<endl;
}
int main()
{
test();
return 0;
}
原理示意图:
从汇编角度看模板函数的推演:
模板参数匹配及显示实例化:
例:
#include<iostream>
using namespace std;
template<class T>
bool IsEqual(const T& left, const T& right)
{
return left == right;
}
int main()
{
cout<<IsEqual(1, 2)<<endl;//正确,编译器确定T为int
//cout<<IsEqual(1, 1.2)<<endl;//错误,编译器无法确定T为int还是double
cout<<IsEqual<int>(1,1.2)<<endl;
//正确,显示实例化,编译器确定T为int,传参过程会发生隐式转换
cout<<IsEqual<double>(1, 1.2)<<endl;
//正确,显示实例化,编译器确定T为double,传参过程会发生隐式转换
return 0;
}
重载模板函数
例:
#include<iostream>
using namespace std;
bool IsEqual(const int& left, const int& right)
{
return left == right;
}
template<class T>
bool IsEqual(const T& left, const T& right)
{
return left == right;
}
template<class T1, class T2>
bool IsEqual(const T1& left, const T2& right)
{
return left == right;
}
int main()
{
cout<<IsEqual(1, 2)<<endl;//调用第一个函数
//当同时存在满足条件的模板函数和一般函数,优先调用一般函数
cout<<IsEqual(1, 1.2)<<endl;//调用第三个函数
cout<<IsEqual<int>(1,1.2)<<endl;//调用第二个函数
//指明调用模板函数
cout<<IsEqual<double>(1, 1.2)<<endl;//调用第二个函数
//指明调用模板函数
return 0;
}
当同时具有满足条件的模板函数和一般函数,优先调用一般函数;
类模板
类模板格式:
template<class 形参1, 形参2, ..., 形参n>
class 类名
{
...
};
例:
template<typename T> //部分代码
class SeqList
{
public :
SeqList();
~ SeqList();
private :
int _size ;
int _capacity ;
T* _data ;
};
template <typename T>
SeqList <T>:: SeqLis()
: _size(0)
, _capacity(10)
, _data(new T[ _capacity])
{
}
template <typename T>
SeqList <T>::~ SeqList()
{
delete [] _data ;
}
void test ()
{
SeqList<int> s1; //s1的类型为SeqList<int>
SeqList<double> s2; //s2的类型为SeqList<double>
}
示意图:
模板参数--容器适配器
例:
#include<iostream>
using namespace std;
template<typename T>
class SeqList
{
private :
int _size ;
int _capacity ;
T* _data ;
};
// template <class T, class Container>
template<class T, class Container = SeqList<T > > // 缺省参数
class Stack
{
public :
void Push (const T& x );
void Pop ();
const T & Top();
bool Empty ();
private :
Container _con ;
};
void test ()
{
Stack<int > s1;
Stack<int , SeqList< int>> s2 ;
Stack<int , SeqList< char>> s3 ;
}
int main()
{
test();
return 0;
}
示意图:
模板的模板参数--容器适配器
例:
#include<iostream>
using namespace std;
template<typename T>
class SeqList
{
private :
int _size ;
int _capacity ;
T* _data ;
};
// template <class T, template<class> class Container>
template<class T, template<class> class Container = SeqList> // 缺省参数
class Stack
{
public :
void Push (const T& x );
void Pop ();
const T & Top();
bool Empty ();
private :
Container<T> _con ;
};
void test ()
{
Stack<int > s1;
Stack<int , SeqList> s2 ;
}
int main()
{
test();
return 0;
}
示意图:
非类型的函数模板/类模板参数
例1:
template<class T, int N>//非类型的函数模板参数
T add(const T& x)
{
return x + N;
}
add<int,10>(1);
例2:
template<class T, size_t MaxSize>//非类型的类模板参数
class SeqList
{
private:
_Data[MaxSize];
};
SeqList<int, 20> s;
模板的全特化/偏特化
函数模板的全特化
例:
#include<iostream>
using namespace std;
typedef struct Student
{
char _name[5];
int _age;
}Stu;
template<class T1, class T2>
bool IsEqual(T1 x, T2 y)
{
return x == y;
}
template<>
bool IsEqual(const Stu& x, const Stu& y)//全特化函数模板
{
return x._age == y._age;
}
int main()
{
cout<<IsEqual(1,2)<<endl;
Stu a = {"aaa",10};
Stu b = {"bbb",10};
cout<<IsEqual(a, b)<<endl;
return 0;
}
函数模板的偏特化
严格来讲,函数模板不支持偏特化,由于可以对函数进行重载,所以可以达到类似于偏特化的效果;
类模板的全特化
例:
#include<iostream>
using namespace std;
template<typename T>//原模板
class A
{
public:
A();
private:
T _a;
};
//未特化的类模板在类外定义成员函数需要模板形参
template<typename T>
A<T>::A()
:_a(0)
{
cout<<"未全特化类模板"<<endl;
}
template<>//全特化类模板
class A<int>
{
public:
A();
private:
int _a;
};
//特化后在类外定义成员函数不需要模板形参
A<int>::A()
:_a(0)
{
cout<<"全特化的类模板"<<endl;
}
int main()
{
A<char> a1;
A<int> a2;
return 0;
}
注意:
- 类的成员函数只声明不定义,在调用时该函数时会出错,例如构造/析构函数;
- 特化后的类模板在类外定义成员函数不需要模板参数;
类模板的偏特化
例1:
#include<iostream>
using namespace std;
template<typename T1,typename T2>
class A
{
public:
A();
private:
T1 _a;
T2 _b;
};
template<typename T1, typename T2>
A<T1,T2>::A()
{
cout<<"A<T1,T2>"<<endl;
}
template<typename T1>//偏特化的类模板
class A<T1,int>
{
public:
A();
private:
T1 _a;
int _b;
};
template<typename T1>
A<T1,int>::A()
{
cout<<"A<T1,int>"<<endl;
}
int main()
{
A<int,char> a1;
A<char,int> a2;
return 0;
}
例2:
#include<iostream>
using namespace std;
template<typename T1,typename T2>
class A
{
public:
A();
private:
T1 _a;
T2 _b;
};
template<typename T1, typename T2>
A<T1, T2>::A()
{
cout<<"A<T1,T2>"<<endl;
}
template<typename T1,typename T2>//偏特化的模板类,模板参数为指针
class A<T1*,T2*>
{
public:
A();
private:
T1 _a;
T2 _b;
};
template<typename T1,typename T2>
A<T1*,T2*>::A()
{
cout<<"A<T1*,T2*>"<<endl;
}
template<typename T1, typename T2>//偏特化的模板类,模板参数为引用
class A<T1& ,T2&>
{
public:
A();
private:
T1 _a;
T2 _b;
};
template<typename T1, typename T2>
A<T1&, T2&>::A()
{
cout<<"A<T1&, T2&>"<<endl;
}
int main()
{
A<int,double> a1;
A<int*, int*> a4;
A<int&, int&> a5;
return 0;
}
注意:
- 偏特化并不仅仅是指特殊部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本;
- 模板的全特化和偏特化都是在已经定义的模板基础之上的,不能单独存在;
- 当模板和特化模板同时满足实例化时,优先匹配特化模板;
模板的分离编译
模板不支持分离编译,原因是当一个模板不被用到时就不应该被实例化出来,在分离编译中,定义模板的文件中并没有实例化出模板,也就是没有该模板的代码,使得链接时找不到该模板的代码,也就无法实例化;
解决办法:将声明和定义放到同一个“.hpp”文件中去;
模板的优/缺点
优点:
1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生;
2. 增强了代码的灵活性;
缺点:
1. 模板让代码变得凌乱复杂,不易维护,编译代码时间变长;
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误;