模板初阶
泛型编程
在我们进行大型程序的编写时往往会遇到一类问题,同一个函数或类我们希望多种类型数据传入时都能完成类似或者相同的功能,但是在C语言中我们很难做到这一点因为我们往往在换了一个数据类型后就要重新写一遍函数,这样耽误我们大量的时间,呢么有没有一种语法在Cpp中能够使让我们的代码成为一种模板,不同的数据类型传入也依然能够执行类似的功能呢?
正所谓世界是由懒人创造的,于是在C++中引入了模板这一概念,同时也正得益与此使我们可以做到泛型编程,是我们的代码极大程度的可以复用。
模板
模板是实现泛型编程的基础,其中又可细分为函数模板和类模板。
函数模板
函数模板是生成一个家族的函数,其中我们可以使用任意类型的参数进行传入。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include <iostream>
using namespace std;
//定义函数模板,T为任意类型
//class 也可替换为 typename
template<class T>
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;
}
int main()
{
int a = 4, b = 5;
double c = 6, d = 7;
Swap(a, b);
Swap(c, d);
cout << a << " " << b << endl;
cout << c << " " << d << endl;
}
5 4
7 6
原理
函数模板在定义完之后并不会直接生成函数,而是在调用时会根据传参类型进行推演,在上面的例子中我们传入了int
类型的参数因此在推演时会将T
转换为int
再进行调用,但有时我们的调用如果出现了让编译器无法推演的情况就会导致报错。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41#include <iostream>
using namespace std;
template<class T>
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;
}
int main()
{
int a = 4, b = 5;
double c = 6, d = 7;
Swap(a, d);
Swap(c, b);
cout << a << " " << b << endl;
cout << c << " " << d << endl;
}
.\test.cpp: In function 'int main()':
.\test.cpp:15:14: error: no matching function for call to 'Swap(int&, double&)'
Swap(a, d);
^
.\test.cpp:4:6: note: candidate: template<class T> void Swap(T&, T&)
void Swap(T& a, T& b)
^~~~
.\test.cpp:4:6: note: template argument deduction/substitution failed:
.\test.cpp:15:14: note: deduced conflicting types for parameter 'T' ('int' and 'double')
Swap(a, d);
^
.\test.cpp:16:14: error: no matching function for call to 'Swap(double&, int&)'
Swap(c, b);
^
.\test.cpp:4:6: note: candidate: template<class T> void Swap(T&, T&)
void Swap(T& a, T& b)
^~~~
.\test.cpp:4:6: note: template argument deduction/substitution failed:
.\test.cpp:16:14: note: deduced conflicting types for parameter 'T' ('double' and 'int')
Swap(c, b);
我们会发现在这样只有一个模板参数T
进行多类型传入就会混淆编译器,导致报错,因此我们可以定义多个模板参数,也可以进行强转使参数类型唯一,不过这里要提到另一种可以让编译器推演出我们想要的函数的方式,显示实例化。
显示实例化
1 | #include <iostream> |
像是这样的情况我们就利用显示实例化给我们的函数模板制定了实例化类型,同时如果类型不匹配,编译器会进行隐式类型转换,如果转换不成功则会报错。
函数模板匹配原则
函数模板可以与非模板函数重名,此时会构成类似于函数重载的情况,并且在函数调用时如果出现重名的函数模板和非模板函数都可以构成匹配则会优先调用非模板函数,而不会对函数模板进行实例化,除非我们利用显示实例化指定必须调用模板函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#include <iostream>
using namespace std;
template<class T>
T Add(T a, T b)
{
cout << "template function" << endl;
return a + b;
}
int Add(int a, int b)
{
cout << "simple function" << endl;
return a + b;
}
int main()
{
int ret = Add(1, 2);//调用普通函数
ret = Add<int>(1, 2);//调用模板函数
cout << ret << endl;
}
simple function
template function
3
但是如果模板函数此时可以提供更好的适配性,则会优先调用模板函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#include <iostream>
using namespace std;
template<class T1, class T2>
T1 Add(T1 a, T2 b)
{
cout << "template function" << endl;
return a + b;
}
int Add(int a, int b)
{
cout << "simple function" << endl;
return a + b;
}
int main()
{
int ret = Add(1, 2.0);//优先调用函数模板
ret = Add<int, double>(1, 2.0);
cout << ret << endl;
}
template function
template function
3
类模板
类模板与函数模板类似,是使用一个模板参数来构造整个类,并且原理也与函数模板类似,只有在类构造对象时才会推演模板参数进行实例化,实例化出我们想要的类。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85#include <iostream>
#include <assert.h>
using namespace std;
template<class T>
class Vector
{
public:
Vector(size_t capacity = 10)
:_pData(new T[capacity])
,_size(0)
,_capacity(capacity)
{
}
~Vector();
//返回size
size_t Size()
{
return _size;
}
//尾插
void Push_back(const T& data)
{
//检查扩容
if(_size >= _capacity)
{//扩容
Reserve(2 * _capacity);
}
_pData[_size] = data;
_size++;
}
//尾删
void Pop_back()
{
_size--;
}
//改变容量
void Reserve(int capacity)
{
if(capacity <= _capacity)
{
return;
}
T* newPData = new T[capacity];
for(int i = 0; i < _size; i++)
{
newPData[i] = _pData[i];
}
_pData = newPData;
_capacity = capacity;
}
T operator[](size_t pos)
{
assert(pos < _size);
return _pData[pos];
}
private:
T* _pData;
size_t _size;
size_t _capacity;
};
//在类外进行函数声明时要加上模板参数
//同时要注意Vector不是一个类,实例化后Vector<T>才是一个类
template<class T>
Vector<T>::~Vector()
{
if(_pData)
{
delete[] _pData;
}
}
int main()
{
Vector<int> arr;
arr.Push_back(1);
arr.Push_back(2);
for(int i = 0; i < arr.Size(); i++)
{
cout << arr[i] << endl;
}
}
1
2
这个例子我们模拟简单实现了一个vector
模板类,并且使用实例化进行使用,类模板与函数模板不同的是类模板往往无法推演出模板参数类型因此需要我们显示实例化使其实例化为一个具体的类才可以进行使用。