C++: C++模板<template>
C++ template content
- 😊前言
- 😁模板
- 💕1、泛型编程
- 😍2、函数模板
- 😒2.1:函数模板概念
- 👌2.2:函数模板的格式
- 😘2.3:函数模板原理
- 😁2.4:函数模板实例化
- 🤞2.5:模板参数的匹配原则
- 👍3、类模板
- 😎3.1:类模板的概念
- 💋3.2:类模板的格式
- 🎉3.3:类模板的实例化
- 🌹4、非类型模板参数
- 😃5、模板的特化
- 🎁5.1:特化的概念
- 👏5.2:函数模板的特化
- 👩5.3:类模板特化
- 🧧5.3.1:类模板全特化
- 🥗5.3.2:类模板偏特化
- 🥫6、模板的分离编译
- 🚗6.1:什么是分离编译
- 🚘6.2:分离编译解决办法
- 💛 7、模板内嵌类型无法识别问题
- 💗8、模板总结*
😊前言
本文介绍模板的机制和基础语法。
😁模板
- 模板就是构建一个通用函数或者类的蓝图
- 在C++中 template被称为参数化类型(parameterized type):称其参数化是因为:类型相关的信息可自
template定义中剥离,称其为类型:是因为每个 class template或 function template
所接受的类型,可以由用户指定。
💕1、泛型编程
如何实现一个泛型编程
#include<iostream>
#include<string>
using namespace std;//在没有泛型编程时,只能使用函数重载逐个写
void Sweap(int& pa, int& pb)
{cout<< "交换之前:"<< pa << " "<< pb <<endl;int temp = pa;pa = pb;pb = temp;cout<< "交换之前:"<< pa << " "<< pb <<endl;
}void Sweap(double& pa, double& pb)
{double temp = pa;pa = pb;pb = temp;
}void Sweap(char& pa, char& pb)
{char temp = pa;pa = pb;pb = temp;
}int main(){int ia = 1;int ib = 2;Sweap(ia,ib);
}
显然,上述代码通过重载的方式来实现不同数据类型的交换,不仅代码的可维护性低,而且一旦需要修改交换函数逻辑,所有的重载函数均需要修改。
为此,我们引入泛型编程的概念,提高代码的复用性,而模板时泛型编程的基础
模板分为:函数模板和类模板
😍2、函数模板
😒2.1:函数模板概念
函数模板代表了一个函数家族,函数模板与类型无关
在使用时被参数化,根据实参类型来推导模板类型参数的特定版本。
👌2.2:函数模板的格式
template<typename T1, typename T2…, typename T3>
函数返回类型 函数名(参数列表){ }
解析:
- 模板定义从template关键字开始,后面的 尖括号<>为模板参数列表
- 尖括号<>中的 typename关键字是模板的类型参数,可以将其看做是类型说明符
- 类型参数可以用来指定函数返回类型或函数参数类型
template<typename T>
void Sweap(T& pa, T& pb){T temp = pa;pa = pb;pb = temp;
}
😘2.3:函数模板原理
原理:
- 函数模板时一个蓝图,它本身并不是函数,是编译器使用生产方式产生特定具体类型函数的模具。
2.所以其实模板就是将本来应该我们重复做的事情,交给了编译器。
在编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。
😁2.4:函数模板实例化
1: 隐式实例化:让编译自己根据实参,推演模板实参的实际类型。
2:显示实例化:在函数名后加上<>, 并在<>中指定模板参数的实际类型。
🤞2.5:模板参数的匹配原则
原则1:一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以实例化为这个非函数模板
原则2:在其他条件相同情况下,在调用时会优先调用非模板函数而不会从该模板产生一个实例。
原则3:模板函数不允许自动类型转换,但是普通函数可以自动类型转换
// 专门处理int的加法函数
int Add(int left, int right)
{return left + right;
}// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{return left + right;
}void Test()
{Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}
👍3、类模板
类模板(calss template)是用来生成类的蓝图(模子)的
😎3.1:类模板的概念
1:与函数模板不同的是:类模板不能隐式地推断模板类型参数的类型
2:实例化时需要显示指定模板参数类型
💋3.2:类模板的格式
template<typename T1, typename T2…, typename Tn>
class 类名 { }
> 原型:
> template<typename T>
> class A
> {
> public:
> A(T a):_a(a) { }
> private:
> T _a;
> }
🎉3.3:类模板的实例化
举例:1: 设计一个Vector ,它装的元素是 T2:先push_back四个元素,然后打印这四个元素3:这里用到了:类模板实例化时显示的将特定类型绑定到模板参数列表中----这个是和模板函数的区别,模板函数可以隐式转换还用到了在类外定义成员函数,必须加上类模板的参数列表获取指针 T* data的值,可以直接 data[i] 这样使用// ---------类模板-------------
template <typename T>
class Vector
{
public:Vector(size_t _capacity = 10):_pData(new T[_capacity]),_size(0),_capacity(_capacity){}// 析构函数,在类外定义~Vector();void PushBack(const T& data);void printfVec(T* datas);T* _pData;
private:size_t _size;size_t _capacity;
};
// 类模板成员函数在类外定义时:必须加上类模板的参数列表
template<typename T>
Vector<T>::~Vector()
{if(_pData) delete[] _pData;_size = 0;_capacity = 0;
}template<typename T>
void Vector<T>::printfVec(T* datas)
{for(int i = 0;i<_size;i++){// 指针datas[i] 其实已经做了提领的操作cout<< datas[i]<< " ";}cout << endl;
}template<typename T>
void Vector<T>::PushBack(const T& data){if(_size == _capacity){T* p = new T[_capacity*2];for(int i = 0; i<_size;i++){p[i] = _pData[i];}_pData = p;_capacity *=2;}_pData[_size] = data;++_size;
}int main(){// 模板实例化,需要显示的指定typenameVector<int> V1(1);V1.PushBack(1);V1.PushBack(2);V1.PushBack(3);V1.PushBack(4);V1.printfVec(V1._pData);
}
🌹4、非类型模板参数
概念
模板类型参数分为两种:类型参数和非类型参数(常量值)
类型参数:
出现在模板参数列表中,<>中存在class或typename的参数类型名称
非类型参数:
使用一个常量作为类(函数)模板的某个参数,在类(函数)模板中可将该参数当做初常量来使用。
1: 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
2:非类型的模板参数必须在编译期就能确定结果。
namespace Array
{
// 模板参数列表中指定了类型,还可以给定“缺省值”
template<class T,size_t N=10>
class array
{
public:T& operator[](size_t index){ return _array[index]; }size_t size()const{return _size;}bool empty()const {return 0==_size; }
private:T _array[N];size_t _size;
};
}void printf_arr(){// 非类型模板参数,必须使用常量/表达式(右值)进行传参Array::array<int,5> arr;for(int i = 0; i<5; i++){arr[i] = 1;}
}
int main(){printf_arr();
}
😃5、模板的特化
🎁5.1:特化的概念
通常情况下:使用模板可以实现一些 与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,那么这些类型就需要特殊处理。
特化分为:函数模板特化和类模板特化。
👏5.2:函数模板的特化
struct Date
{int _year = 1;int _month = 1;int _day = 1;
};template<class T>
bool ISEqual(T left, T right)
{return left == right;
}// 函数模板特化(针对某些类型的特殊化处理)
bool ISEqual(Date* left, Date* right)
{return left->_year == right->_year&& left->_month == right->_month&& left->_day == right->_day;
}int main(){//int ia = 1;// int ib = 2;//Sweap(ia,ib);// 模板实例化,需要显示的指定typename// Vector<int> V1(1);// V1.PushBack(1);// V1.PushBack(2);// V1.PushBack(3);// V1.PushBack(4);// V1.printfVec(V1._pData);// printf_arr();cout << ISEqual(1,3)<< endl;// 如果没有特化函数模板,那么比较的就是指针(比较地址),那这样是无意义的struct Date date1;date1._year = 2022;date1._month = 3;date1._day = 4;struct Date date2;date2._year = 2022;date2._month = 3;date2._day = 4;cout<< ISEqual(&date1,&date2)<< endl;}