c++模板编程可以使你的程序更加的优雅和简介,在减少了代码量的优势下不说,运用得当也可以极高提高运行效率和开发效率。
定义一个函数模板的格式并不复杂,形式如下:
例:template <typename type>
关键字template告诉编译器,将要定义一个模板,尖括号里面的内容相当于函数的参数列表。当模板被调用时,type将会被具体的类型值(如int、double等)所取代。template是声明模板的关键字;typename用来标识的形参名用来表示一个虚拟类型,当使用函数模板时,会将其具体化为具体的类型,typename关键字也可以用class替换。
模板是干什么的呢?
int sum(int a, int b)
{
return a + b;
}
double sum(double a, double b)
{
return a + b;
}
对于上面的2个函数,虽然它们的返回值和参数类型不一致,但函数名和函数体完全一致。C++的模板就可以解决这种问题,则会大大提高代码的可重用性。
模板是C++支持参数化多态的工具,它可以实现类型参数化,即使类中的数据成员或成员函数的参数、返回值取得任意类型。模板是一种对类型进行参数化的工具,能减少程序员编写类型无关的代码。C++的模板可分为函数模板和类模板,通过函数模板具体化的函数称为模板函数,通过类模板具体化的类称为模板类。
那么我们把这个函数转化为模板:
template <typename T>
T sum(T a, T b)
{
return a + b;
}
当我们定义A和B的类型为int时,T sum(T a, T b)只是一个“函数模板”,使用它是需要先实例化为一个“模板函数”(如 int sum(int,int)
)。编译器在编译阶段,为每个调用次模板的代码生成了一个函数。函数模板并不是函数,,它仅在编译时根据调用此模板函数时传递的实参类型来生成具体的函数体!若函数模板没有被调用责不生成任何代码也不对模板代码作任何语法检查。
接下来我们用模板实现一个stack.
template<typename T>
class Stack
{
public:
Stack();
bool isEmpty();
bool isFull();
bool push(const T& item);
bool pop(T & item);
private:
enum { MAX = 10 };
T items[MAX];
int top;
};
template<typename T>
Stack<T>::Stack()
{
top = 0;
}
template<typename T>
bool Stack<T>::isEmpty()
{
return top == 0;
}
template<typename T>
bool Stack<T>::isFull()
{
return top == MAX;
}
template<typename T>
bool Stack<T>::push(const T& item)
{
if (top < MAX)
{
items[top++] = item;
return true;
}
else
return false;
}
template<typename T>
bool Stack<T>::pop(T & item)
{
if (top > 0)
{
item = items[--top];
return true;
}
else
return false;
}
在程序中仅包含模板并不能生成模板类,必须请求实例化。为此,需要声明一个类型为模板类的对象,方法是使用所需的具体类型替换泛型名。例如,下面代码创建两个栈,一个用于存储int,另外一个用于存储string对象。
Stack<int> a;
Stack<string> b;
看到上述声明后,编译器将按照Stack<T>模板来生成两个独立的类声明和两组独立的类方法。它们会分别使用int和string替换T。使用的算法必须与类型一致。
函数模板的重载
template <typename T>
void fun(T a)
{
cout<<"template fun: "<<a<<endl;
}
void fun(int a)
{
cout<<"int fun: "<<a<<endl;
}
int main()
{
fun('a');
fun(2);
return 0;
}
函数模板的重载也称之为函数的特例化。一般是在定义的函数模板无法满足所有的情况时,对特定的类型进行特例化。如果一个调用,即匹配普通函数,又能匹配模板函数的话,则优先匹配普通函数。因此,当我们模板特例化的时候,会先匹配特例化的函数。
默认类型的模板参数
模板的另一项新特性是,可以为类型参数提供默认值:
template<typename T1,typename T2 = int>
如果省略T2的值,编译器将会使用int。
A<double,double> m; 这个T1和T2都是double。
A<double> m; 这个T1是double,T2是int。
虽然可以为类模板类型参数提供默认值,但不能为函数模板参数提供默认值。
类模板与友元
模板的友元分为三类:
- 非模板友元
- 约束模板友元,即友元类型取决于类被实例化时的类型
- 非约束模板友元,即友元的所有具体化都是类的每一个具体化的友元
1.模板类的非模板友元函数
template <typename T>
class A
{
public:
friend void count();
};
上述声明让count()成为了模板所有实例化的友元。它将会是A<int>和A<string>的友元。count()函数不是通过对象调用的那么它将如何访问A对象呢?
template <typename T>
class A
{
public:
friend void count(A<T> &);
};
当我们生成具体化时A<int> a;时,将会用int代替T。也就是说,带A<int>参数的count( )将成为A<int>类的友元。同样,带A<double>参数的count( )将是count( )的一一个重载版本,它 是A<double>类的友元。注意,count( )本身并不是模板函数,而只是使用一一个模板作参数。这意味着必须为要使用的友元定义显式具体化。
C++11可以使用模板提供一系列别名。如下所示:
template<tupename T>
using arrtype = array<T,12>;
这将arrtype定义为一个模板别名,可以使用它来指定类型。
arrtype<double> gall;
//gall类型为array<double,12>