文章目录
一、模板类型
1.1 函数模板
template<typename T> //这里用<class T>也可以
T increment(T x){
return T+1;
}
increment(5); // 6
increment(3.4); //4.4
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。
1.2 类的方法模板
class A{
template <typename T>
T sum(T from, T to, T step) {
T res = from;
while ((from += step) < to) {
res += from;
}
return res;
}
.......
};
1.3 类模板
template<class T>
class ArrayOf2 {
public:
T& operator[](size_t i) {
return a_[i];
}
const T& operator[](size_t i) const {
return a_[i];
}
T sum() const {
return a_[0] + a_[1];
}
private:
T a_[2];
};
//调用:
ArrayOf2<int> i;
i[0] = 1;
i[2] = 5;
std::cout << i.sum(); // 6
当类模板实例化时所有的数据成员都被实例化,但是成员函数不会自动被实例化,只有当它被调用或取地址时,才被实例化
1.4 非类型模板参数
template <typename T, size_t N> class Array {
public:
T& operator[](size_t i) {
if (i >= N) throw std::out_of_range("Bad index");
return data_[i];
}
private:
T data_[N];
};
Array<int, 5> a; // OK
cin >> a[0]; // 等待用户输入
Array<int, a[0]> b; // Error,编译失败
非类型参数模板的值要求在编译期是常量或常量表达式,所以上面会编译失败
- 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
- 非类型的模板参数必须在编译期就能确认结果
二、模板的特化
2.1 函数模板特化
template<>
bool IsEqual<char*>(char*& left, char*& right)
{
if(strcmp(left, right) > 0)
return true;
return false;
}
2.2 类模板特化–全特化
全特化即是将模板参数列表中所有的参数都确定化
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;
}
2.3 类模板特化–偏特化
偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。比如对于以下模板类:
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); // 调用特化的指针版本
}
三、模板不支持分离编译
a.hpp a.cpp main.cpp
因为每个.cpp文件单独编译,编译a.cpp时没有调用,所以不会生成符号,而编译main.cpp时,只能找到声明的地方,需要等到链接的时候在其他.o或者.so库中找到定义的地方,而此时a.o中又没有定义的地方,就会出现链接错误
解决方法:
-
提前实例化模板
在模板定义文件中将所有要用到的模板函数都进行实例化 -
定义实现全部放在同一个.h文件中
当某个.cpp文件用到某个模板时,包含了相应的头文件,而头文件中同时由模板的定m义和声明,在cpp文件中使用就相当于对这个模板进行了实例化(.cpp文件依赖.h文件编译成.o文件,cpp文件中实例化,h文件中进行定义和声明),这样就可以使用模板了