【C++】深入了解模板

模板参数分类
  • 类型形参:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
  • 非类型形参:用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
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 0==_size;
	}
	private:
	T _array[N];
	size_t _size;
};

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

模板的特化

在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。

template<class T>
bool Isqual(T& left, T& right)
{
	return left == right;
}

void Test()
{
	char *p1 = "hello";
	char *p2 = "world";

	if(Isqual(p1,p2);
		cout << p1 << endl;
	else
		cout << p2 <<endl;
}
函数模板特化的步骤
  • 先有一个基础的函数模板;
  • 关键字template后有“<>”<>为空;
  • 函数名后跟<>,<>中指定需要特化的类型;
  • 必须要和模板函数的基础参数类型完全相同,如果不同编译器会报错。
template<>
bool Isqual<char*>(char*& left, char*& right)
{
	if(strcmp(left,right)>)
		return true;
return false;
}

一般函数模板在遇到不能处理或处理有误的类型时,为了实现简单通常是直接给出。

类模板特化
全特化

是指将模板参数类表中所以参数都确定化。

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;
}
偏特化

任何针对模板参数进一步进行条件限制设计的特化版本。

template<class T1, class T2>
class Data
{
public:
	Data()
	{
		cout <<"Data<T1,T2>"<<endl;
	}
private:
	T1 _d1;
	T2 _d2;
};
两种表现形式
  • 部分特化:将模板参数类表中的一部分参数特化。
template<class T>
class Date<T,int>
{
	public:
		Data()
		{
			cout << "Data<T,int>"<<endl;
		}	
	private:
		T _d1;
		int _d2;
};
  • 参数更进一步限制:针对模板参数更进一步的条件限制所设计出来的一个特化版本。
template<typename T1, typename T2>
class Data<T1*, T2*>
{
	public:
		Data()
		{	
			cout << "Data<T1*,T2*>" << endl;
		}
	private:
	T1 _d1;
	T2 _d2;
};
类模板特化应用
  • 使用memcpy拷贝(深拷贝会出错)
template<class T>
void Copy(T* dst,const T* src,size_t size)
{
	memcpy(dst,src,sizeof(T)*size);
}
int main()
{
	string s1[3] = {"he","ll","o"};
	string s2[3];
	Copy(s1,s2,3);
}
  • 使用赋值方式拷贝(代码效率低)
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];
	}
}

结论:遇到内置类型用memcpy,遇到自定义循环用赋值。

  • 增加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];
		}
	}
}

缺陷:用户需要根据拷贝类型串参数,出错的可能性会增加。

  • 使用函数区分内置类型与自定义类型
    内置类型的个数是确定的,将内置类型结合到一起,就可以确定了。
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[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来确定所拷贝对象的实际类型,与内置类型集合中对比,分辨出其类型。
缺陷:枚举需要将所有类型遍历一遍,效率较低。

  • 类型萃取
    两个类分别代表内置类型与自定义类型
//代表内置类型
struct TrueType
{
	static bool Get()
	{
		return true;
	}
};

//代表自定义类型
struct FalseType
{
	static bool Get()
	{
		return false;
	}
};
模板分离

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

//例子
//a.h
#include <iostream>
template<class T>
T add(const T& x, const T& y);
//a.cpp
template<class T>
T add(const T& x,const T& y)
{
	return x+y;
}
//main.cpp
#include "a.h"
int main()
{
	add(1,2);
	add(1.2,1.5);
	return 0;
}

解决方法:
将声明与定义放到一个文件里面;
模板定义的位置显式实例化。

模板的优点

模板复用了代码,节省资源,更快的迭代开发,C++的STL就是因此产生的;增强了代码的灵活性。

模板的缺点

模板会导致代码膨胀的问题,也会使编译时间边长;
出现模板编译错误是,报错会比较乱,定位不准。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值