一、函数模板
1、定义函数模板
template
有两种方式定义函数模板,typename 关键字和 class 关键字,两者是一样的。
2、实例化
编译器将泛型的函数模板转化为具体类型函数的过程称为函数模板的实例化。
以如下函数模板为例:
template
2.1 隐式实例化 (implicit instantiation)
app_max
可以通过< > 指定实例化的模板函数类型,也可以只使用函数名,由编译器根据实际参数类型推断实例化模板函数,如 app_max(5.0f, 8.0f)可以推断为 float app_max<float>(float, float) 函数类型。
2.2 显式实例化 (explicit instantiation)
template
显式实例化告诉编译器以何种数据类型生成模板函数实例,可以实现函数模板文件的单独编译。比如,temp.cc 如果不显式实例化是不能编译成目标文件的。显式实例化后,编译器会在temp.o文件中分别生成 app_max<int> 和 app_max<double>函数的相应二进制代码。在 http://main.cc 中通过 extern 命令可以强制 main 函数使用显式实例化后的app_max<int> 代码,而不会通过隐式实例化在main.cc文件中再实例化一个同样的代码。
// temp.hh
2.3 模板定制(specilization)与重载
template
注意模板定制与显式实例化在语法上只是多了一个 < >标识符。编译器会按照新的函数定义来实例化 std::string 类型的模板函数。
2.4 隐式实例化的选择原则
当同名函数同时存在函数模板、模板定制和普通函数定义时,隐式实例化会优先选择类型匹配的普通函数,如果没有与之类型匹配的普通函数,会优先选择模板定制,最后才会通过函数模板实例化函数实现。
template
二、类模板
1、定义类模板
template
2、实例化
2.1 隐式实例化
X
xi 是一个 X<int> 对象,编译器会实例化 X <int> 类,由于程序只调用了 X<int>::f(),所以实例化的 X<int> 类中只有 f() 成员函数而没有 g() 成员函数。
pxi 是一个X <int>类型的指针,单独存在时(即没有声明别的 X <int>对象),编译器并不会创建 X<int>实例。
pxf 是一个 X <float>类型的指针,且动态分配了内存,编译器会创建 X <float>实例,且只实例化 g() 成员函数。
2.2 显式实例化
// temp.cc
2.3 定制
template
三、可变参数模板
//a class template with zero or more type parameters
例如,可变参数模板求最小值
#include
四、关于模板参数(template parameters)和模板实参 (template arguments)
4.1 模板参数
模板参数用于函数模板和类模板的声明和实例化,位于 < > 内部:
//class template declaration
模板参数有以下三类:
4.1.1 非类型参数 (nontype parameter)
声明阶段可以是 int, 指针常量,常引用以及枚举类型,实例化阶段是编译期常量。
template
4.1.2 类型模板参数 (type parameter)
// 无默认类型模板参数
对于类型模板参数,typename 和 class 可以互换使用。
4.1.3 模板型模板参数 (template parameter)
#include
模板型模板类型可以将模板类数据类型(如 STL 模板类)作为模板参数,只能用 class 关键字不能用 typename 关键字。
4.1.4 默认模板参数
template<class T1, class T2 = int> class X; // line1
template<class T1 = float, class T2> class X; // line2
template<class T1, class T2> class X { // line3
public:
T1 a;
T2 b;
};
X <int> x1; // <int,int>
X <float> x2; // <float,int>
X<> x3; // <float, int>
X<double, char> x4; // <double,char>
默认模板参数声明必须放在原始模板之前, 如 line1 和 line2 必须在line3 上方。如果一个模板参数有默认类型,则它右侧的所有参数都必须有默认类型。
4.2 模板实参
4.2.1 非类型实参 (nontype template arguments)
//part 1: define template with non-type template parameters
4.2.1 类型实参 (type template arguments)
template
4.2.2 模板型实参 (template template arguments)
//primary class template X with template type parameters
c 实例化了一个 Y<X>类型的模板类。编译器首先用实参 X 代替型参 V, 在 class Y 内部,X<int, char> 和 X<char,char>分别隐式实例化为 X <int , char> (部分定制类)和 X <char, char> 原始类。
4.3 关于模板参数 (parameter) 和模板实参 (argument) 的总结
![d6358abdef64008eb9949995c9558438.png](https://i-blog.csdnimg.cn/blog_migrate/da241b96aa410bf06e5b018116e8e41d.jpeg)
五、type_traits 库
type_traits (或者 boost) 库定义了一系列的模板结构体,可以用来给出模板实参的类型信息。如 std::is_pointer<T>::value 可以判断 T 是否为指针,std::is_same<T1,T2>::value 可以判断T1,T2 类型是否相同。
namespace
首先定义 copier 结构体非类型参数模板,其中定义了静态模板函数 do_copy(); 然后分别实例化了 copier<false> 和 copier<true> 模板类对象。
最后,定义了 copy() 模板函数,根据 T1 和 T2 的类型,选择性调用 copier<flase>:: do_copy和 copier<true>::do_copy() 函数。
六、模板元编程(Template Metaprogramming)
模板元编程是指利用模板,以其他程序为操作对象的程序,比如编译器。利用元编程,可以实现代码在编译期的优化,提高运行效率。
6.1 编译期计算
常量表达式
#include
常量静态成员
#include
const static 成员变量会在编译期间赋值,也即递归计算在编译期间而非程序运行期间完成。
fact
6.2 编译期代码优化
#include
利用静态模板函数可以实现 for 循环的展开,即将 for(i=0;i<5;i++){ret+=a[i]*b[i];}在编译期间优化成(a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3] + a[4]*b[4])。