模板进阶
目录
一、非类型模板参数
二、模板的特化
1、函数模板特化
函数模板的特化步骤:
函数模板特化的调用顺序
2、类模板特化
全特化
偏特化
类模板特化实例
三、模板分离编译
一、非类型模板参数
类中的模板参数分为类类型和非类类型。
template<class T,size_t N=10>
void fun(T& x, size_t n=N)
{cout << "void fun(T& x, size_t n)" << endl;
}
上面我们定义了一个size_t类型的模板参数N,N可以作为常量参与具体的运算。
需要注意的是,在C++11标准及以前,仅支持整数类型作为类的非类类型模板参数。
这里还有个需要注意的地方:在使用模板T进行传参的时候最好进行传引用传参,因为T可能是自定义类型,而对自定义类型进行非引用传参会导致拷贝增加使得程序效率下降。
二、模板的特化
1、函数模板特化
函数模板的特化步骤:
1. 必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号<>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇 怪的错误。
下面有一代码:
template<class T>
int add(T& x)
{return x * 2;
}
假如我们就只想让double类型的变量乘3,那么我们就可以使用模板特化。
函数模板特化的调用顺序
函数调用的时候会选着最符合其参数特征的那个。
template<class T, size_t N = 10>
void fun(T& x, size_t n = N)
{cout << "void fun(T& x, size_t n)" << endl;
}template<class T1,class T2>
int add(const T1& x, const T2& y)
{cout << "const T1& x, const T2& y" << endl;return x +y;
}template<>
int add<double,int>(const double& x, const int& y)
{cout << "const double& x, const int& y" << endl;return x +y;
}template<>
int add<double, double>(const double& x, const double& y)
{cout << "const double& x, const double& y" << endl;return x + y;
}int main()
{add(1, 1);add(1.0, 1);add(1, 1.0);return 0;
}
但实际上我们更加偏向于直接给出处理特殊类型的函数,这样比模板特化更加简单。
2、类模板特化
全特化
全特化即是将模板参数列表中所有的参数都确定化。
template<class T1,class T2>
class text
{
public:text(){cout << "class T1,class T2" << endl;}
private: int _a;int _b;
};template<>
class text<int, int>
{
public:text(){cout << "int,int" << endl;}
private:int _a; int _b;
};template<>
class text<int, double>
{
public:text(){cout << "int,double" << endl;}
private:int _a;int _b;
};int main()
{text < double, double> a1;text<int, int> a2;text<int, double> a3;return 0;
}
偏特化
与全特化不同,偏特化仍需要保留 “待推导的模板参数”。
eg:
template<class T>
class text<int, T>
{
public:text(){cout << "int,T" << endl;}
private:int _a;int _b;
};template<class T1,class T2>
class text<T1*, T2*>
{
public:text(){cout << "T1*, T2*" << endl;}
private:int _a;int _b;
};
类模板特化实例
我们特化一个fun类来用于比较数据之间的大小关系
我们用最下面的sort模板。
template<class T1, class T2>
class Less
{
public:bool operator()(const T1& x, const T2& y){return x._a > y._a;}
};template<class T1, class T2>
class text;template<>
class text<int, int>
{
public:text(){cout << "int,int" << endl;}text(const int& x, const int& y):_a(x), _b(y){ }friend ostream& operator<<(ostream& out, const text& x);friend Less<text<int, int>, text<int, int>>;private:int _a; int _b;
};ostream& operator<<(ostream& out, const text<int, int>& x)
{out << x._a << "-" << x._b << endl;return out;
}
int main()
{text<int,int> A(1, 2);text<int, int> B(3, 4);text<int, int> C(5, 6);text<int, int> D(7, 8);vector<text<int, int>> h;h.push_back(C);h.push_back(B);h.push_back(D);h.push_back(A);for (auto e : h){cout << e;}cout << "排序后:" << endl;sort(h.begin(), h.end(), Less<text<int,int>, text<int, int>>());for (auto e : h){cout << e;}return 0;
}
运用Less类模板中的()符号重载,我们何以控制sort排序的方式:
当模板参数为int指针类型时,需特别注意Less类中的()重载函数的参数:
下面还是直接给出正确写法吧(不是作者懒)
template<>
class Less
{
public:bool operator()( int* const x, int* const y){return *x > *y;}
};
为什么const加在*号右边?因为此时Less类的模板参数为指针类型,const修饰的是模板,如果const加在*左边则不是修饰指针本身!
三、模板分离编译
先看下面的代码:
// 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
#include"a.h"
int main()
{Add(1, 2);Add(1.0, 2.0);return 0;
}
当我们运行上面代码时会出现链接错误。
这是因为当有文件在main.cpp展开时并没有看到模板的实例化,也就导致在链接的过程中找不到函数地址而报错。
解决办法:
1. 将声明和定义放到一个文件 "xxx.cpp" 里面或者xxx.h。
2. 模板定义的位置显式实例化。
// 显示实例化
template <class T>
class text
{//…………
};template class text<int>; // 关键语法:template + class + 模板名<具体类型>;
相比较于将声明与定义不分离,模板显示实例化更加麻烦,我们平常更加推荐使用第一种声明与定义不分离方式。