BruceFan's Blog

Stay hungry, stay foolish

0%

C++模板与泛型编程

模板是C++中泛型编程的基础,一个模板就是一个创建类或函数的蓝图。

函数模板

我们可以定义一个通用的函数模板(function template),而不是为每个类型都定义一个新函数。compare的模板版本如下:

1
2
3
4
5
6
7
template <typename T>
int compare(const T &v1, const T &v2)
{
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}

模板定义以关键字template开始,后跟一个模板参数列表(template parameter list),这是一个逗号分隔的一个或多个模板参数(template parameter),用尖括号包围。
实例化函数模板:

1
2
3
4
5
// 实例化出int compare(const int&, const int&)
cout << compare(1, 0) << endl; // T为int
// 实例化出int compare(const vector<int>&, const vector<int>&)
vector<int> vec1{1, 2, 3}, vec2{4, 5, 6};
cout << compare(vec1, vec2) << endl; // T为vector<int>

类型参数前必须使用关键字classtypename

1
2
3
// 在模板参数列表中,typename和class没有什么不同
template <typename T, class U>
T calc(const T&, const U&);

类模板

类模板(class template)是用来生成类的蓝图。与函数模板的不同之处是,编译器不能为类模板推断模板参数类型,必须在模板名后面的尖括号中提供额外信息——用来替代模板参数的模板实参列表。

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
#include <iostream>
using std::cout;
using std::endl;
#include <string>
using std::string;
#include <memory>
using std::shared_ptr;
using std::make_shared;
#include <vector>
using std::vector;

using std::initializer_list;
using std::out_of_range;

template <typename T> class Blob {
public:
typedef T value_type;
typedef typename vector<T>::size_type size_type;
Blob();
Blob(initializer_list<T> il);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(const T &t) { data->push_back(t); }
// 移动版本
void push_back(T &&t) { data->push_back(move(t)); }
void pop_back();
T &back();
T &operator[](size_type i);
private:
shared_ptr<vector<T>> data;
void check(size_type i, const string &msg) const;
};

// 默认构造函数
template <typename T>
Blob<T>::Blob(): data(make_shared<vector<T>>()) { }

// 构造函数
template <typename T>
Blob<T>::Blob(initializer_list<T> il):
data(make_shared<vector<T>>(il)) { }

// 检查数组是否越界
template <typename T>
void Blob<T>::check(size_type i, const string &msg) const
{
if (i >= data->size())
throw out_of_range(msg);
}

// 删除最后一个元素
template <typename T>
void Blob<T>::pop_back()
{
check(0, "pop_back on empty Blob");
data->pop_back();
}

// 返回最后一个元素
template <typename T>
T &Blob<T>::back()
{
check(0, "back on empty Blob");
return data->back();
}

// 重载下标运算符,返回元素的引用
template <typename T>
T &Blob<T>::operator[](size_type i)
{
check(i, "subscript out of range");
return (*data)[i];
}

int main()
{
Blob<int> squares = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (size_t i = 0; i != squares.size(); ++i)
squares[i] = i * i;
cout << squares.back() << endl;
return 0;
}