模版的作用
先看看下面的代码感受一下模版的作用吧
#include<iostream>
using namespace std;void Swap(int& a, int& b)
{int temp = a;a = b;b = temp;
}void Swap(double& a, double& b)
{double temp = a;a = b;b = temp;
}int main()
{int a = 1, b = 2;Swap(a, b);cout << "a= " << a << " b= " << b << endl;double c = 1.1, d = 2.2;Swap(c, d);cout << "c= " << c << " d= " << d << endl;return 0;
}
在上面的代码中,我们可以发现Swap函数有2个,而且构成了重载.
这两个Swap函数结构及其相似,有点冗余.
我们开始思考,是否可以只写一个函数来实现交换功能呢?
答案是可以.
我们使用模版就可以轻松缩短代码!
请看:
#include<iostream>
using namespace std;template<class T>
void Swap(T &a, T& b)
{T temp = a;a = b;b = temp;
}int main()
{int a = 1, b = 2;Swap(a, b);cout << "a= " << a << " b= " << b << endl;double c = 1.1, d = 2.2;Swap(c, d);cout << "c= " << c << " d= " << d << endl;return 0;
}
模版的语法
//写法1
template<class T1,class T2>//可以有很多种不同的类型,T3,T4......Tn,但是前面都要加上class修饰//写法2
template<typename T1,typename T2>//typename和class的作用是一样的
template<class + 模版名,class + 模版名2......,class + 模版名n>
class和typename可以混用,但是不推荐.
例子
#include<iostream>
using namespace std;int add(int a, int b)
{return a + b;
}template<class T>
T add(T a, T b)
{return a + b;
}int main()
{int a = 1, b = 2;int c = add(a, b);cout << c << endl;string str1 = "abc";string str2 = "123";string str3 = add(str1, str2);cout << str3 << endl;return 0;
}
这个例子中有两个add函数,c和str3调用的add函数会是同一个吗?
答案是否定的.
调试就会发现:
c调用的是int add
str3调用的是T add
这是为什么呢?
当两个函数都能调用的时候,编译器会优先调用非模版函数.
模版的分类
模版分为函数模版和类模版,我上面举的两个例子都是函数模版,下面我们来看看类模版.
类模板
在c语言,我们可以手搓一个栈(stack),栈的类型是固定的,比如说是int类型的栈.
如果我们想要double类型的栈,就得重写一个或者改写原来的栈.
这样很不方便.
在c++中,我们可以自己写一个有类模版的栈,就不必担心栈的类型了.
在下面的例子中我们有一个类Stack(很简陋,没有完全实现栈的功能),却能定义int和double类型的两个Stack.
#include<iostream>
using namespace std;template<typename T>
class Stack
{public :Stack(size_t capacity = 4){_array = new T[capacity];_capacity = capacity;_size = 0;} void Push(const T& data);
private:T* _array;size_t _capacity;size_t _size;
};template<class T>
void Stack<T>::Push(const T& data)
{// 扩容_array[_size] = data;++_size;
} int main()
{Stack<int> st1; // intStack<double> st2; // doublereturn 0;
}
注意事项:
类模版的声明和定义不能分离,不能在.c头文件声明而在.cpp文件定义,这样会报错!!
哪里使用了模版,就在哪里就实现它.
非类型模版参数
非模版类型参数就是在template里面有未知的模版类型,也有已知的类型
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用,如下面的例子中的a
template<class T,size_t a = 1>
class test
{
public:T arr[a];
};int main()
{test<string, 10> t;return 0;
}
注意事项:
浮点数、类对象以及字符串是不允许作为非类型模板参数的
模版的特化
函数模版的特化
一个模版适用于大部分类型,但也有一些类型无法解决.
如下面的例子:
int类型可以用这个模版,但是string类型不能用
所以针对string类型,我们就要有一个特化
特化的语法
template<>
返回类型 函数名<参数类型>(参数列表) {// 函数体
}
特化的类型
全特化
全特化即是将模板参数列表中所有的参数都确定化。
template<class T1, class T2>
class Data
{public :Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};
template<>
class Data<int, char>
{public :Data() { cout << "Data<int, char>" << endl; }
private:int _d1;char _d2;
};
void TestVector()
{Data<int, int> d1;Data<int, char> d2;
}
偏特化
偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。(只有部分参数特化)
template<class T1, class T2>
class Data
{public :Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{public :Data() { cout << "Data<T1, int>" << endl; }
private:T1 _d1;int _d2;
};
类模版特化
我们将以日期类作为例子,讲解类模版特化 .
下面这篇博客有日期类的具体实现代码,而本篇只提供头文件,让大家大概知道日期类的功能.
C++类和对象(5)——运算符重载(以日期类为例)-CSDN博客
请看下面的代码
template<class T>
struct Less
{bool operator()(const T& x, const T& y) {return x < y;}
};int main()
{Date d1(2025, 1, 19);Date d2(2024, 1, 19);Date d3(2025, 1, 18);vector<Date> v;v.push_back(d1);v.push_back(d2);v.push_back(d3);sort(v.begin(), v.end(),Less<Date>());for (auto& e : v){cout << e << endl;}return 0;
}
这里的Less类里的()重载可以正常运行,但是不适用于下面这种类型
此时需要我们特例化类模版
template<>
struct Less<Date*>
{bool operator()(const Date* x, const Date* y) {return *x < *y;}
};