Cpp|| 模板进阶

一.非类型模板参数

在这里插入图片描述

//定义一个模板类型的静态数组
template<class T,size_t N=10>

class Array{
public:
	T& operator[](size_t index){
		return _array[index];
	}

	const T& operator[](size_t index)const{
	return _array[index];
	}
	size_t Size()const{
		return _size;
	}
	bool Empty()const{
	return _size==0;
	}
private:
T _array[N];
size_t _size;
}

注意:

  • 1.浮点数、类对象以及字符串是不允许作为非类型模板参数
  • 2.非类型的模板参数必须在编译期就能确认结果

二.模板的特化

1.模板特化概念
  • 使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,如下代码就需要对模板进行特化
template<class T>
bool IsEqual(T& left,T& right){
	return left==right;
}

void Test(){
	char* p1="hello";
	char* p2="world";
	if(IsEqual(p1,p2))
		cout<<p1<<endl;
	else
		cout<<p2<<endl;
}
  • 原模板类的基础上,针对特殊类型所进行特殊化的实现方式.
  • 模板特化中分为函数模板特化类模板特化
2.函数模板特化
  • 步骤
  • 1.必须要先有一个基础的函数模板
  • 2.关键字template后面接一对空的尖括号<>
  • 3.函数名后加上一对尖括号,尖括号中指定需要特化的类型
  • 4.函数形参表:必须要和模板函数的基础参数类型完全相同,如果不同编辑器可能会报错
template<>
bool IsEqual<char*>(char*& left,char*& right){
	if(strcmp(left,right)>0)
	return true;
return false;

一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简答通常都是将该函数直接给出

bool IsEqual(char* left,char* right){
	if(strcmp(left,right)>0)
	return true;
return false;
}
3.类模板特化
3.1全特化
  • 全特化:将模板参数类表中所有的参数都确定化

前提:首先需要有一个基础的类模板

#include<iostream>
using namespace std;
template<class T1, class T2>
class Data
{
public:
 Data() {cout<<"Data<T1, T2>" <<endl;}
private:
 T1 _d1;
 T2 _d2;
};
template<>
class Data<int, char> {
//有一个基础的模板类型
//且参数都有确定的类型
public:
 Data() {
 cout<<"Data<int, char>"<<endl;
 }
private:
 T1 _d1;
 T2 _d2;
};
void TestVector()
{
 Data<int, int> d1;
 Data<int, char> d2;
}

int main(){
	TestVector();
	return 0;
};
/*
全特化:template<>
class 类名<类型1,类型2>
*/
  • 运行结果

在这里插入图片描述

3.2:偏特化
  • 任何指针模板参数进一步进行条件限制设计的特化版本.
template<class T1,class T2>

class Data{
	public:
	Data(){
		cout<<"Data<T1,T2>"<<endl;
	}
	private:
	T1 _d1;
	T2 _d2;
	};
  • 偏特化的两种表现方式
  • 部分特化:将模板参数类表中的一部分参数特化
//将第二个参数特化为int
template <class T1>
class Data<T1,int>{
	public:
		Data(){
		cout<<"Data<T1,int>"<<endl;
		}
	private:
	T1 _d1;
	int _d2;
	}
  • 参数进一步的限制
  • 针对模板参数更进一步的条件限制所设计出来的一个特化版本
//两个参数偏特化为指针类型
template <typename T1,typename T2>
class Data<T1*,T2*>{
	public:
	Data(){
	cout<<"Data<T1*,T2*>"<<endl;
	}
private:
T1 _d1;
T2 _d2;
};

//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
 Data(const T1& d1, const T2& d2)
 : _d1(d1)
 , _d2(d2)
 {
 cout<<"Data<T1&, T2&>" <<endl;
 }
 
private:
 const T1 & _d1;
 const T2 & _d2; 
 };
void test2 () 
{
 Data<double , int> d1; // 调用特化的int版本
 Data<int , double> d2; // 调用基础的模板 
 Data<int *, int*> d3; // 调用特化的指针版本
 Data<int&, int&> d4(1, 2); // 调用特化的引用版本
}

在这里插入图片描述

  • 总结
  • 类模板特化:

全特化:

template<>
  class<类型1,类型2>
  • 如下例
template <>
class Date<int,char>{
};

特化的前提条件:首先需要有一个基础的类模板

偏特化:

1.部分特化:只特化部分类型,

template<class T>
calss Date<T,int>{

};

2.对模板参数做进一步限制:

 template <class T1,class T2>
 class Date<T1*,T2*>{
 };

三:类模板特化应用之类型萃取

1.使用memcpy拷贝
template <class T>
void Copy(T* dst,const T* src,size_t size){
	memcpy(dst,src,sizeof(T)*size);
}
int main()
{
 string strarr1[3] = {"11", "22", "33"};
 string strarr2[3];
 Copy(strarr2, strarr1, 3);
}

上述代码虽然对任意类型的空间都可以进行拷贝,但是拷贝自定义类型的对象可能会出错,因为自定义类型对象有可能会涉及到深拷贝(比如string),而memcpy属于浅拷贝,如果对象中涉及到资源管理,就只能用赋值

2.使用赋值方式拷贝
template<class T>
void Copy(T* dst, const T* src, size_t size) {
 for(size_t i = 0; i < size; ++i)
 {
 dst[i] = src[i];
 }
}
3.增加bool类型区分自定义与内置类型
template<class T>
void Copy(T* dst, const T* src, size_t size, bool IsPODType) {
 if(IsPODType)
 memcpy(dst, src, sizeof(T)*size);
 else
 {
 for(size_t i = 0; i < size; ++i)
 dst[i] = src[i];
 }
}
  • 通过多增加一个参数,就可以将两种拷贝的优势体现结合起来.
  • 但缺陷是:用户需要根据所拷贝元素的类型去传递第三个参数,那出错的可能性就增大了.
4.使用函数区分内置于自定义类型

因为内置类型的个数是确定的,可以将所有内置类型集合在一起,如果能将所拷贝对象的类型确定下来,在内置类型集合中查找其是否存在既可确定所拷贝类型是否为内置类型

//
// POD: plain old data 平凡类型(无关痛痒的类型)--基本类型
// 指在C++ 中与 C兼容的类型,可以按照 C 的方式处理。
//
// 此处只是举例,只列出个别类型
bool IsPODType(const char* strType) {
 const char* arrType[] = {"char", "short", "int", "long", "long long", "float", 
"double", "long double"};
 for(size_t i = 0; i < sizeof(array)/sizeof(array[0]); ++i)
 {
 if(0 == strcmp(strType, arrType[i]))
 return true;
 }
 
 return false; }
template<class T>
void Copy(T* dst, const T* src, size_t size) {
 if(IsPODType(typeid(T).name()))
 memcpy(dst, src, sizeof(T)*size);
 else
 {
 for(size_t i = 0; i < size; ++i)
 dst[i] = src[i];
 }
}
  • 通过typeid来确定所拷贝对象的实际类型,然后再在内置类型集合中枚举其是否出现过,既可确认所拷贝元素的类型为内置类型或者自定义类型.但缺陷是:枚举需要将所有类型遍历一遍,每次比较都是字符串的比较,效率比较低
5.类型萃取

为了将内置类型与自定义类型区分开,给出下面两个分别代表内置类型与自定义类型

//代表内置类型
struct TrueType{
	static bool Get(){
		return true;
	}
};
//代表自定义类型
struct FalseType{
	static bool Get(){
		return false;
	}
};
  • 1.使用类模板和模板特化技术定义类型萃取类typetraits定义类型
  • 2.对于内置类型,通过模板特化自定义类型的typetraits,内部定义类型truetype
  • 3.编译期通过输入的类型生成对应的typetraies,调用TrueType或者flasetype的get()方法确定具体的类型是否为自定义类型,根据返回结果决定返回的方式
  • 4.优点:效率高,编译时确定类型,不占用运行时间

给出以下类模板,将来用户可以按照任意类型实例化该类模板

template <class T>
struct TypeTraits{
	typedef FalseType IsPODType;
};
  • 实例化
/*T为int:TypeTraits<int>已经特化过,程序运行时就会使用已经特化过的TypeTraits<int>, 该类中的IsPODType刚好为类TrueType,而TrueType中Get函数返回true,内置类型使用memcpy方式拷贝
 T为string:TypeTraits<string>没有特化过,程序运行时使用TypeTraits类模板, 该类模板中的IsPODType刚好为类FalseType,而FalseType中Get函数返回true,自定义类型使用赋值方式拷贝
 
*/
#include<iostream>
using namespace std;
template<class T>
void Copy(T* dst, const T* src, size_t size) {
 if(TypeTraits<T>::IsPODType::Get())
 memcpy(dst, src, sizeof(T)*size);
 else
 {for(size_t i = 0; i < size; ++i)
 dst[i] = src[i];
 }
}
int main()
{
 int a1[] = {1,2,3,4,5,6,7,8,9,0};
 int a2[10];
 Copy(a2, a1, 10);
 
 string s1[] = {"1111", "2222", "3333", "4444"};
 string s2[4];
 Copy(s2, s1, 4);
 return 0; 
 }

四:模板分离编译

1.什么是分离编译

一个程序(项目)由若干个源文件共同实现,而灭个源文件单独编译生成目标文件,最后将所有的文件链接起来形成单一的可执行文件的过程

  • 如下例中,在头文件中进行声明,源文件中完成定义
//a.h中进行声明
template<class T>
T Add(const T& left,const T& right);

//a.cpp中完成定义
template <class T>
T Add(const T& left,const T& right){
	return left+right;
}
//main.cpp

int main(){
Add(1,2);
Add(1.0,2.0);
return 0;
}
  • 该编译过程的分析

在这里插入图片描述

  • 解决办法
  • 1.将声明和定义放到一个文件"xxx.hpp"里面 或者xxx.h 中也可
  • 2.模板定义的位置显式实例化,

五:模板总结

在这里插入图片描述

1.优点
  • 模板代码复用,节省资源,更快的迭代开支,C++的标准模板库(STL)因此而产生
  • 增强代码的灵活性
2.缺点
  • 模板会导致代码膨胀问题,也会导致编译时间变长
  • 出现模板编译错误时,错误信息非常凌乱,不易定位错误
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值