目录
- 函数模板
- 类模板
- 非类型模板参数
- 模板特化
- 全特化
- 偏特化
- 模板分离编译
- 模板总结
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础
函数模板
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
函数模板格式
template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}
用不同类型的参数使用函数模板时&#xff0c;称为函数模板的实例化。模板参数实例化分为&#xff1a;隐式实例化和显式实例化。
函数模板的实例化
隐式实例化&#xff1a;让编译器根据实参推演模板参数的实际类型
实现一个交换函数&#xff0c;在以前用c语言写的时候&#xff0c;针对不同的类型我们需要写多个交换函数&#xff0c;在C&#43;&#43;中有了模板后并不需要&#xff0c;他会通过实际的参数来推断函数模板的参数类型&#xff0c;进而实例化出一份合适的函数供你使用
template <class T>
void Swap(T &num1, T& num2)
{T tmp &#61; num1;num1 &#61; num2;num2 &#61; tmp;
}void functest()
{int a &#61; 10, b &#61; 20;Swap(a,b);cout << "a :" << a << "b :" << b << endl;float c &#61; 10.55f, d &#61; 3.14f;Swap(c, d);cout << "c :" << c << "d :" << d << endl;
}
流程示例图&#xff1a;
在编译器编译阶段&#xff0c;对于模板函数的使用&#xff0c;编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如&#xff1a;当用double类型使用函数模板时&#xff0c;编译器通过对实参类型的推演&#xff0c;将T确定为double类型&#xff0c;然后产生一份专门处理double类型的代码&#xff0c;对于字符类型也是如此
注意&#xff1a;typename是用来定义模板参数关键字&#xff0c;也可以使用class(切记&#xff1a;不能使用struct代替class)
编译器根据实参的类型推模板参数的类型&#xff0c;这种也叫隐士类型转换&#xff0c;而显示实例化又是怎么样的呢
显式实例化&#xff1a;在函数名后的<>中指定模板参数的实际类型
int a &#61; 10, b &#61; 20;
Swap<int>(a, b);
float c &#61; 10.55f, d &#61; 3.14f;
Swap<float>(c, d);
显示指定模板参数类型后&#xff0c;就不再需要编译器根据实参的类型去推断模板参数T的类型了&#xff0c;模板参数类型显示指定是啥就是啥
补充&#xff1a;
1、 一个非模板函数可以和一个同名的函数模板同时存在&#xff0c;而且该函数模板还可以被实例化为这个非模板函数
2、 对于非模板函数和同名函数模板&#xff0c;如果其他条件都相同&#xff0c;在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数&#xff0c; 那么将选择模板
3、模板函数不允许自动类型转换&#xff0c;但普通函数可以进行自动类型转换
类模板
类模板的定义格式
template<class T1, class T2, ..., class Tn>
class 类模板名
{
};
类模板的实例化
类模板实例化与函数模板实例化不同&#xff0c;类模板实例化需要在类模板名字后跟<>&#xff0c;然后将实例化的类型放在<>中即可&#xff0c;类模板名字不是真正的类&#xff0c;而实例化的结果才是真正的类
部分模拟实现vector类&#xff0c;后继在加以补充
namespace mzt
{template<class T>class vector {public:vector() : _a(nullptr),_size(0),_capacity(0){ }void push_back(const T& data){if (_capacity &#61;&#61; _size) {size_t newcapacity &#61; _capacity &#61;&#61; 0 ? 4 : _capacity * 2;T* tmp &#61; new T[newcapacity];assert(tmp);_a &#61; tmp;_capacity &#61; newcapacity;}_a[_size&#43;&#43;] &#61; data;}T& operator[](size_t pos) {assert(pos < _size);return _a[pos];}size_t getsize() {return _size;}private:T* _a;size_t _size;size_t _capacity;};
}void func1()
{mzt::vector<int> a;a.push_back(1);a.push_back(2);a.push_back(3);a.push_back(4);for (size_t i &#61; 0; i < a.getsize(); i&#43;&#43;) {cout << " " << a[i];}cout << endl;
}
非类型模板参数
模板参数分类&#xff1a;类型形参、非类型形参。
类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。
非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将该参数当成常量来使用
namespace mzt
{template<class T &#61; int, size_t N &#61; 10> class Array {public:void f() {}private:T* arr[N];};}
int main()
{mzt::Array< > a; return 0;
}
注意&#xff1a;
1、 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
2、 非类型的模板参数必须在编译期就能确认结果
模板特化
通常情况下使用模板可以实现一些与类型无关的代码&#xff0c;但对于一些特殊类型的可能会得到一些错误的结果&#xff0c;此时&#xff0c;就需要对模板进行特化。即&#xff1a;在原模板类的基础上&#xff0c;针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化
函数模板特化
template<class T>
bool IsEqual(const T& left, const T& right)
{return left &#61;&#61; right;
}int main()
{char arr1[] &#61; "hello";char arr2[] &#61; "hello";bool ret &#61; mzt::IsEqual(arr1, arr2);cout << ret << endl;const char* p1 &#61; "hello";const char* p2 &#61; "hello";ret &#61; mzt::IsEqual(p1, p2); cout << ret << endl;
}
同一字符串比较的结果确是不同&#xff0c;因为指针只会指向一块已经存在的空间&#xff0c;就是字符串的起始地址&#xff0c;而如果是数组的话他会创建两块不相同的空间&#xff0c;但是不符合预期的效果
解决办法模板特化
template< >
bool IsEqual<const char *>(const char* const& left, const char* const& right)
{return strcmp(left, right) &#61;&#61; 0;
}
bool IsEqual(const char* left, const char* right)
{return strcmp(left, right) &#61;&#61; 0;
}void func()
{char arr1[] &#61; "hello";char arr2[] &#61; "hello";bool ret &#61; mzt::IsEqual(arr1, arr2);cout << ret << endl;const char* p1 &#61; "hello";const char* p2 &#61; "hello";ret &#61; mzt::IsEqual<const char*>(p1, p2);cout << ret << endl;}
函数模板特化总结&#xff1a;
1、必须要先有一个基础的函数模板
2、关键字template后面接一对空的尖括号<>
3、函数名后跟一对尖括号&#xff0c;尖括号中指定需要特化的类型
4、函数形参表: 必须要和模板函数的基础参数类型完全相同&#xff0c;如果不同编译器可能会报一些奇怪的错误
template<>
bool IsEqual(const char* const& left, const char* const& right)
{return strcmp(left, right) &#61;&#61; 0;
}
类模板特化
全特化
即是将模板参数列表中所有的参数都确定化。
namespace mzt
{template<class T1, class T2> class Array {public:Array() { cout << "" << endl; }};template<>class Array<int,int>{public:Array() { cout << "" << endl; }private:};template<>class Array<int, double>{public:Array() { cout << "" << endl; }private:};template<>class Array<int*, int*>{public:Array() { cout << "" << endl; }private:};template<>class Array<int&, int&>{public:Array() { cout << "" << endl; }private:};}int main()
{ mzt::Array<int, int>a1;mzt::Array<int, double>a2;mzt::Array<int*, int*>a3;mzt::Array<int&, int&>a4;mzt::Array<double, int>a5;return 0;
}
偏特化
偏特化&#xff1a;任何针对模版参数进一步进行条件限制设计的特化版本。比如对于以下模板类
部分特化
namespace mzt
{template<class T1, class T2> class Array {public:Array() { cout << "" << endl; }};template<class T1>class Array<T1, int>{public:Array() { cout << "" << endl; }};template<class T1>class Array<T1, double>{public:Array() { cout << "" << endl; }};}int main()
{ mzt::Array<int,int> a1; mzt::Array<int, double> a2;mzt::Array<char, char> a3;return 0;
}
参数更进一步的限制
偏特化并不仅仅是指特化部分参数&#xff0c;而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
namespace mzt
{template<class T1, class T2> class Array {public:Array() { cout << "" << endl; }};template<class T1, class T2>class Array<T1*, T2*>{public:Array() { cout << "" << endl; }};template<class T1, class T2>class Array<T1&, T2&>{public:Array() { cout << "" << endl; }};
}int main()
{ mzt::Array<int,int> a1;mzt::Array<int*, double*> a2;mzt::Array<char&, char&> a3;return 0;
}
模板分离编译
func1.cpp
template<class T>
T Sub(T& left, T& right)
{return left - right;
}
func1.h
template<class T>
T Sub(T& left, T& right);
test.c
int main()
{ int a &#61; 10, b &#61; 20;int ret &#61; Sub(a,b);cout << ret << endl;return 0;
}
模板分离编译后在链接的过程中会发现找不到Sub函数
解决办法显示实例化
template<class T>
T Sub(T& left, T& right)
{return left - right;
}
template
int Sub<int>(int& left, int& right);
解决办法二&#xff1a;不需要显示实例化&#xff0c;函数模板的定义和声明都放到.h文件中
template<class T>
T Sub(T& left, T& right)
{return left - right;
}
模板总结
【优点】
- 模板复用了代码&#xff0c;节省资源&#xff0c;更快的迭代开发&#xff0c;C&#43;&#43;的标准模板库(STL)因此而产生
- 增强了代码的灵活性
【缺陷】
- 模板会导致代码膨胀问题&#xff0c;也会导致编译时间变长
- 出现模板编译错误时&#xff0c;错误信息非常凌乱&#xff0c;不易定位错误
3.模板不支持分离编译