背景

C++是一个易学难精的语言,因为其包含的内容太多了。比较基础的是面向对象,这也是c++设计的初衷。另外比较重要的两块是泛型编程和标准模板库。
以前写代码的时候,面向对象的特性经常会使用,不过继承和封装这些特性使用较少,其实这些特性在很多开源库中会看到,但自己写代码时可能是没有遇到比较大和复杂的项目,通常就是几个功能模块的简单聚合,因此这些特性很少使用。但看过很多开源代码,这些特性差不多也了解了,也会有意实现一些demo。标准模板库也偶尔使用,这里面的包括了数据结构,迭代器,算法等,对开发帮助极大。唯独泛型编程使用较少,看c++书籍到这一部分也会直接跳过。所以最近打算深入学习一下C++模板泛型编程的内容。

基本概念

模板使用在需要对多个类型进行同样操作的函数或者类中。可将多个进行相同操作,但数据类型不同的代码进行合并,减少代码量。所以模板是一种对类型进行参数化的工具。

模板可以使用在类和函数中,只要在类或者函数定义前加入template即可,class即为模板参数,可以有多个。
类模板

template <class T>
class TT{
public:
T t;
};

函数模板

template <class T>
T add(T &a, T &b)
{
return a + b;
}

模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行。

模板参数

模板参数会有三种,类型参数,非类型参数和模板参数(不是笔误,就是模板作为模板参数)。

类型参数

类型参数就是最常见的形式

template <class T>
T add(T &a, T &b)
{
return a + b;
}

非类型参数

非类型参数就是把整型,指针和引用作为模板参数。其中整型使用最多,使用时整型参数必须是一个在编译期可以计算的值。

template<class T,int MAXSIZE> class Stack{}

在使用时

Stack<int,5> stack;

代表这个stack最多放5个元素。

模板参数

作为模板参数的参数任然是一个模板。比如如下给矩阵分配内存空间的Allocator类,但是矩阵元素类型不确定,可以在矩阵结构体中指定,然后调用Allocator去分配内存。

template <class T> struct Allocator{}
template <template<class> class Allocator> struct Matrix{}

模板的特殊情况

模板特化

模板特化实现编译期的条件选择。在定义了一个模板函数或模板类后,如果有需要例外处理的类型,需要进行模板特化。

template<class T>
bool Isequal(T& p1, T& p2){
return p1 == p2;
}

但对于字符串不能这么比较,因此对字符串类型进行特化

template<>
bool Isequal<char*>(char*& p1, char*& p2){
return strcmp(p1, p2) == 0;
}

模板递归

模板递归主要是把在运行期执行的操作转移的编译期。比如计算斐波那契数列的递归,这里面也用到了模板特化,模板参数使用的非类型参数。

template<int N>
struct Fib
{
enum { Result = Fib<N - 1>::Result + Fib<N - 2>::Result };
};

//模板特化
template <>
struct Fib<1>
{
enum { Result = 1 };
};

//模板特化
template <>
struct Fib<0>
{
enum { Result = 0 };
};

模板元编程

模板元编程和泛型编程一样,都是使用c++语言的模板特性。模版元编程完全不同于普通的运行期程序,因为模版元程序的执行完全是在编译期,并且模版元程序操纵的数据不能是运行时变量,只能是编译期常量,不可修改。另外它用到的语法元素也是相当有限,不能使用运行期的一些语法,比如if-else、for和while等语句都不能用。比如上面计算斐波那契数列的模板类就是比较简单的模板元编程。