模板
模板概念
模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数, 从而实现了真正的代码可重用性。模版可以分为两类,一个是函数模版,另外一个是类模版。
函数模板
函数模板一般形式如下:
template <typename T>
返回类型 函数名(形参表){
//函数体
}
eg:
template <typename y>
T min(T x, T y) {
return (x < y) ? x : y;
}
类模板
定义一个类模板:
template <typename T>
class 类名 {
//类定义
};
eg:
//hpp
template <typename T1, typename T2>
class MyClass{
public:
MyClass(T1 a, T2 b);
void show();
private:
T1 t1;
T2 t2;
};
template <typename T1, typename T2>
MyClass<T1, T2>::MyClass(T1 a, T2 b):t1(a),t2(b) {}
template <typename T1, typename T2>
void MyClass<T1,T2>::show() {
cout << "t1: " << t1 << " t2: " << t2 << endl;
}
//cpp
void main()
{
myClass<int,int> class1(3,5);
class1.show();
}
可变参数模板
普通模板只可以采取固定数量的模板参数。然而,有时候我们希望模板可以接收任意数量的模板参数,这个时候可以采用可变参数模板。对于可变参数模板,其将包含至少一个模板参数包,模板参数包是可以接收0个或者多个参数的模板参数。相应地,存在函数参数包,意味着这个函数参数可以接收任意数量的参数。
- 模板参数包:
template<typename... Args>
class tuple;
Args标识符的左侧使用了省略号,在C++11中Args被称为“模板参数包”,表示可以接受任意多个参数作为模板参数,编译器将多个模板参数打包成“单个”的模板参数包。
- 函数参数包:
template<typename... T>
void func(T... args);
args被称为函数参数包,表示函数可以接受多个任意类型的参数。
在C++11标准中,要求函数参数包必须唯一,且是函数的最后一个参数;模板参数包则没有,当声明一个变量(或标识符)为可变参数时,省略号位于该变量的左侧。当使用参数包时,省略号位于参数名称的右侧,表示立即展开该参数,这个过程也被称为解包。
可变参数类模板使用规则
一个可变参数tuple类模板定义如下:
template<typename... T>
class Tuple{
};
可以用任意数量的类型来实例化tuple:
Tuple<> t0;
Tuple<int> t1;
Tuple<int, string> t2;
如果想避免出现用0个模板参数来实例化可变参数模板,可以这样定义模板:
template<typaname T, typename... types>
class Tuple{};
此时在实例化时,必须传入至少一个模板参数,否则无法编译。
可变参数函数模板使用规则
template<typename... types>
void func(types... args);
func();
func(1);
func(3.4, "hello");
可变参数模板注意事项
- 对于类模板来说,可变模板参数包必须是模板参数列表中的最后一个参数。
- 对于函数模板来说,则没有这个限制。
template<typename... T, typename U>
clasee Invaild{};
//以上是非法定义,永远无法推断出U的类型
template<typename... T, typename U>
void vaild(U u, T... args); //合法
void invaild(T... args, U u); // 非法
模板特例化
用于对特定的数据类型执行不同的代码,而不是通用模板,这种就是模板特例化。
函数模板特例化
当特例化一个函数模板时,必须为原模板中的每个模板参数都提供实参。使用关键字template后跟一个空尖括号<>,即template <>,以指出我们正在特例化一个模板。
template<typename T>
void fun(T a){
cout << "The main template fun():" << a << endl;
}
template<>
void fun(int a){
cout << "Specialized template for int type:" << a << endl;
}
int main()
{
fun<char>('a');
fun<int>(10);
fun<float>(9.15);
return 0;
}
输出结果:
The main template fun(): a
Specialized template for int type: 10
The main template fun(): 9.15
对于除int型外的其他数据类型,都会调用通用版本的函数模板fun(T a);对于int型,则会调用特例化版本的fun(int a)。注意,一个特例化版本的本质是一个实例,而非函数的重载。因此,特例化不影响函数匹配。
类模板特例化
template<typename T>
class Test{
public:
void print() {
cout << "General template object" << endl;
}
};
template<>
class Test<int>{
public:
void print() {
cout << "Specialized template object" << endl;
}
};
int main()
{
Test<int> a;
Test<char> b;
Test<float> c;
a.print();
b.print();
c.print();
return 0;
}
输出结果:
Specialized template object
General template object
General template object
另外,与函数模板不同,类模板的特例化不必为所有模板参数提供实参。我们可以只指定一部分而非所有模板参数,这种叫做类模板的 偏特化 或 部分特例化(partial specialization)。例如,C++标准库中的类vector的定义:
template <typename T, typename Allocator>
class vector
{
/*......*/
};
// 部分特例化
template <typename Allocator>
class vector<bool, Allocator>
{
/*......*/
};
在vector这个例子中, 一个参数被绑定到bool类型,而另一个参数仍未绑定需要由用户指定。注意,一个类模板的部分特例化版本仍然是一个模板,因为使用它时用户还必须为那些在特例化版本中未指定的模板参数提供实参。