模板参数分类
- 类型形参:出现在模板参数列表中,跟在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就是因此产生的;增强了代码的灵活性。
模板的缺点
模板会导致代码膨胀的问题,也会使编译时间边长;
出现模板编译错误是,报错会比较乱,定位不准。