在C++中,我们可以使用函数重载来提高代码的灵活性,比如当实现某些相同的功能时,可以重载不同版本的函数,来根据不同的情况实现对应的操作,但是对于每种类型都需要重载一次。而如果使用模版,就可以使代码不再局限于参数的类型,更好的实现代码复用。
模版分为函数模版和类模版
函数模版
函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计。
函数模板的声明形式为:
template<typename(或class)T>
<返回类型><函数名>(参数表)
{
}
在这里面,class与typename的作用其实是完全一样的,在最早的时候,使用class来表示其后面的是通用类型,但是由于class这个关键字可能会让人们把模版函数和模版类弄混,所以后来又引入了typename。typename和class都是表示后面的类型是通用类型。
当我们定义好了函数模版后,就可以调用其来实现相关的功能。
函数模版的调用有两种,分别是显示类型推导和隐式类型推导
显示类型推导就是在调用的时候在函数名后标明类型,而隐式类型推导就是不标明类型,根据参数的类型自动推导。
我们在定义函数模版的时候也可以直接指定模版默认类型。
自动推导类型的优先级高于默认类型
手动推导类型的优先级高于自动推导类型
另外要注意的一点是,当使用多个参数的模版时,实际参数的类型得是一样的才可以
如果不一样必须手动推导才可以
因为靠自动推导编译器不知道是什么类型,无法通过编译。
模版的特化:
模板特化可以为模板定义一个或多个特定类型的实现。当模板函数被这些特定类型之一调用时,编译器将使用相应的特化版本而不是通用模板。
#include <iostream>
using namespace std;
// 通用模板
template <typename T>
void print(T value) {
cout << "General template: " << value << endl;
}
// 特化版本,针对 int 类型
template <>
void print<int>(int value) {
cout << "Specialized template for int: " << value << endl;
}
// 特化版本,针对 const char* 类型
template <>
void print<const char*>(const char* value) {
cout << "Specialized template for const char*: " << value << endl;
}
int main() {
print(123); // 调用特化版本,int
print("Hello, world"); // 调用特化版本,const char*
print(123.456); // 调用通用模板
return 0;
}
类模版
当我们使用类模版,可以将这个类中的成员变量定义为任意类型的数据。
类模版的定义格式为:
template <classT>
class foo
{
}
#include <iostream>
using namespace std;
// 类模板定义
template <class T>
class Box {
private:
T content;
public:
void setContent(T value) {
content = value;
}
T getContent() {
return content;
}
};
int main() {
Box<int> intBox;
intBox.setContent(123);
cout << "Integer Box Content: " << intBox.getContent() << endl;
Box<string> stringBox;
stringBox.setContent("Hello Templates!");
cout << "String Box Content: " << stringBox.getContent() << endl;
return 0;
}
在上面的代码中,定义了一个类模版,并且定义了两个类,这两个类的成员变量分别为int和string
也可以定义其他数据结构比如栈或者队列的存储类型。
需要注意的是,函数模板可以使用隐式类型推导,但类模板不可以,必须是显示推导,所以在定义模版的时候,必须指定类型,即便是无参构造也要指定。
类模版的继承
类模板也可以参与继承,可以定义一个基于模板的类(父类),然后让其他模板类(子类)继承自这个父类模板。
template <typename T>
class Base {
public:
void baseMethod() {
// 基类方法实现
}
};
template <typename T>
class Derived : public Base<T> { // 注意这里
public:
void derivedMethod() {
// 派生类方法实现
}
};
排成类模版从具体化的基类模板继承
派生类模板不仅可以从一个泛型基类模板继承,还可以从一个特定类型的基类模板实例(即模板特化)继承。
template <>
class Base<int> {
// 对 int 类型的特化版本
};
template <typename T>
class Derived : public Base<int> { // 继承自 Base<int>
// 实现...
};
使用不同的模版参数继承
派生类模板可以使用和基类模板不同的模板参数,甚至可以根据派生类的模板参数来指定基类的模板参数。
template <typename T, typename U>
class Base {
// 基类模板,接受两种类型的模板参数
};
template <typename T>
class Derived : public Base<T, int> { // 派生类只接受一个模板参数,但将两个参数传递给基类
// 实现...
};