配接器
STL 配接器通过包装容器、迭代器或函数,扩展其行为,实现参数绑定、逻辑取反、函数组合等功能。
配接器
STL 配接器(Adapters)概览
STL 中的 配接器(adapter) 是一种设计模式,用于将某个组件的接口转换成另一种接口,从而实现不同组件间的协作。它的作用类似“轴承”或“转换器”,让原本不兼容的类能够一起运作。
STL 的配接器可分为三大类:
类型 | 作用对象 | 功能 |
---|---|---|
Container Adapter | 容器 | 改变容器接口,使其呈现不同的行为(如 stack、queue) |
Iterator Adapter | 迭代器 | 改变迭代器行为(如插入、逆序、绑定到流) |
Function Adapter / Functor Adapter | 仿函数(函数对象) | 修饰或组合仿函数、普通函数、成员函数,增强表达能力 |
容器配接器(Container Adapter)
STL 提供的容器配接器主要有:
- stack:基于 deque 封装,只提供栈操作接口(LIFO)。
- queue:基于 deque 封装,只提供队列操作接口(FIFO)。
- 它们只是改变了底层容器的接口风格,而非底层数据结构。
迭代器配接器(Iterator Adapter)
迭代器配接器用于改变迭代器的行为,包括以下几类:
插入迭代器(Insert Iterators)
- 类型:
back_insert_iterator
→push_back()
front_insert_iterator
→push_front()
insert_iterator
→ 任意位置插入
- 辅助函数:
back_inserter(container)
front_inserter(container)
inserter(container, iterator)
- 将迭代器的赋值操作转换为插入操作,使算法能方便地将元素写入容器。
逆序迭代器(Reverse Iterators)
- 将迭代器的前进/后退方向反转:
operator++
→ 后退operator--
→ 前进
- 常用方法:
rbegin()
/rend()
配合reverse_iterator
使用
- 对于从尾部开始处理数据的算法非常方便。
流迭代器(I/O Stream Iterators)
类型:
istream_iterator<T>
→ 绑定输入流(如cin
)ostream_iterator<T>
→ 绑定输出流(如cout
)
可用于将 STL 算法直接与流对象交互:
1 2
ostream_iterator<int> out(cout, " "); copy(v.begin(), v.end(), out);
这种迭代器可以扩展到文件、网络、缓存等各种输入/输出装置。
仿函数配接器(Functor / Function Adapters)
仿函数配接器用于修饰、组合或绑定函数对象,从而构造复杂表达式。
主要功能
- 绑定(bind):固定某个参数值
- 否定(negate):逻辑取反
- 组合(compose):组合多个函数对象
- 修饰普通函数 / 成员函数:
ptr_fun()
→ 修饰普通函数mem_fun_ref()
/mem_fun()
→ 修饰成员函数
示例
找出序列中不小于 12 的元素个数
1
count_if(v.begin(), v.end(), not1(bind2nd(less<int>(), 12)));
less<int>()
是二元仿函数bind2nd(..., 12)
将第二个参数固定为 12not1(...)
取逻辑否定 → “不小于 12”
对序列每个元素执行 (v + 2) * 3
并输出
1
2
3
transform(v.begin(), v.end(), out, compose1(
bind2nd(multiplies<int>(), 3),
bind2nd(plus<int>(), 2)));
compose1(f, g)
表示f(g(x))
bind2nd
固定参数transform
将结果输出到迭代器out
修饰普通函数 / 成员函数
1
2
for_each(v.begin(), v.end(), ptr_fun(print)); // 普通函数
for_each(vInt.begin(), vInt.end(), mem_fun_ref(&Int::print1)); // 成员函数
注意事项
- 可配接的仿函数必须继承自
unary_function
或binary_function
。 - 函数或成员函数必须通过配接器处理后,才能与 STL 算法无缝结合。
- 不符合接口要求的函数无法直接使用(如
for_each
)。
容器配接器(Container Adapters)
容器配接器是一种作用于已有容器之上的 接口封装器,它隐藏了底层容器的部分接口,只暴露符合特定数据结构原则的操作。
STL 中典型的容器配接器包括 stack 和 queue。
stack
- 底层容器:默认由
deque
构成(也可以使用其他序列容器)。 - 模板定义:
1
2
3
4
5
6
template <class T, class Sequence = deque<T>>
class stack {
protected:
Sequence c; // 底层容器
// ...
};
- 特点:
- 只开放符合 栈(LIFO) 原则的接口,如:
push()
:压入元素pop()
:弹出元素top()
:访问栈顶元素empty()
/size()
- 隐藏底层
deque
的其他接口,如随机访问和迭代器。 - 因此,
stack
是一个 容器配接器,通过封装底层容器实现栈行为。
- 只开放符合 栈(LIFO) 原则的接口,如:
queue
- 底层容器:默认由
deque
构成(也可以使用其他序列容器)。 - 模板定义:
1
2
3
4
5
6
template <class T, class Sequence = deque<T>>
class queue {
protected:
Sequence c; // 底层容器
// ...
};
- 特点:
- 只开放符合 队列(FIFO) 原则的接口,如:
push()
:入队pop()
:出队front()
/back()
:访问队头和队尾empty()
/size()
- 隐藏底层
deque
的其他接口。 - 因此,
queue
也是一个 容器配接器,通过封装底层容器实现队列行为。
- 只开放符合 队列(FIFO) 原则的接口,如:
迭代器配接器(Iterator Adapter)
插入迭代器(Insert Iterators)
三种 insert iterators 的核心思想是:每个适配器内部都维护一个用户指定的容器。当对迭代器执行赋值操作(*it = value
)时,实际上会转化为对容器调用 push_back()
、push_front()
或 insert()
。至于 operator++
和 operator*
,它们虽然定义了,但没有实际的“移动位置”或“取值”语义,只是为了满足迭代器的接口要求,通常返回自身或一个哑对象。因此,这类迭代器不具备遍历能力,仅能用来输出数据,所以其类型被标记为 输出迭代器(output_iterator_tag)。
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// ----------------------------
// back_insert_iterator
// 将迭代器的赋值操作转为 push_back(),从容器尾部插入元素
// ----------------------------
template <class Container>
class back_insert_iterator {
protected:
Container* container; // 底层容器指针
public:
typedef output_iterator_tag iterator_category; // 输出迭代器
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
// 构造函数:将迭代器绑定到容器
explicit back_insert_iterator(Container& x) : container(&x) {}
// 赋值操作 -> push_back
back_insert_iterator<Container>& operator=(const typename Container::value_type& value) {
container->push_back(value); // 核心:调用容器的 push_back
return *this;
}
// 无效操作符,返回自身
back_insert_iterator<Container>& operator*() { return *this; }
back_insert_iterator<Container>& operator++() { return *this; }
back_insert_iterator<Container>& operator++(int) { return *this; }
};
// 辅助函数,方便使用
template <class Container>
inline back_insert_iterator<Container> back_inserter(Container& x) {
return back_insert_iterator<Container>(x);
}
// ----------------------------
// front_insert_iterator
// 将迭代器的赋值操作转为 push_front(),从容器头部插入元素
// 注意 vector 不支持 push_front()
// ----------------------------
template <class Container>
class front_insert_iterator {
protected:
Container* container;
public:
typedef output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
explicit front_insert_iterator(Container& x) : container(&x) {}
// 赋值操作 -> push_front
front_insert_iterator<Container>& operator=(const typename Container::value_type& value) {
container->push_front(value); // 核心:调用容器的 push_front
return *this;
}
// 无效操作符,返回自身
front_insert_iterator<Container>& operator*() { return *this; }
front_insert_iterator<Container>& operator++() { return *this; }
front_insert_iterator<Container>& operator++(int) { return *this; }
};
// 辅助函数
template <class Container>
inline front_insert_iterator<Container> front_inserter(Container& x) {
return front_insert_iterator<Container>(x);
}
// ----------------------------
// insert_iterator
// 将迭代器的赋值操作转为 insert(),在指定位置插入元素
// 并将迭代器右移,方便连续插入
// ----------------------------
template <class Container>
class insert_iterator {
protected:
Container* container;
typename Container::iterator iter; // 插入位置
public:
typedef output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
insert_iterator(Container& x, typename Container::iterator i)
: container(&x), iter(i) {}
// 赋值操作 -> insert,并将迭代器右移
insert_iterator<Container>& operator=(const typename Container::value_type& value) {
iter = container->insert(iter, value); // 插入
++iter; // 迭代器右移,紧跟新插入元素
return *this;
}
// 无效操作符,返回自身
insert_iterator<Container>& operator*() { return *this; }
insert_iterator<Container>& operator++() { return *this; }
insert_iterator<Container>& operator++(int) { return *this; }
};
// 辅助函数
template <class Container, class Iterator>
inline insert_iterator<Container> inserter(Container& x, Iterator i) {
return insert_iterator<Container>(x, i);
}
核心思想:
back_insert_iterator
→ 尾部插入front_insert_iterator
→ 头部插入insert_iterator
→ 指定位置插入
迭代器行为:
operator*
、operator++
、operator++(int)
被关闭,无实际作用。定义为 输出迭代器(output_iterator_tag)。
辅助函数:
back_inserter()
、front_inserter()
、inserter()
用来方便创建适配器对象。
逆序迭代器(Reverse Iterators)
逆向迭代器的作用是将迭代器的移动方向倒转。
- 对于普通迭代器:
++it
向前,--it
向后。 - 对于逆向迭代器:
++rit
实际向后移动,--rit
实际向前移动。
当 STL 算法使用逆向迭代器时,会从序列尾部到头部处理元素。
容器提供的 rbegin()
和 rend()
几乎所有双向序列容器都提供:
1
2
reverse_iterator rbegin(); // 指向最后一个元素
reverse_iterator rend(); // 指向第一个元素的前一个位置
示例:vector
、list
、deque
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
// vector 示例
template <class T, class Alloc = alloc>
class vector {
public:
typedef T value_type;
typedef value_type* iterator;
typedef reverse_iterator<iterator> reverse_iterator;
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
};
// list 示例
template <class T, class Alloc = alloc>
class list {
public:
typedef __list_iterator<T, T&, T*> iterator;
typedef reverse_iterator<iterator> reverse_iterator;
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
};
// deque 示例
template <class T, class Alloc = alloc, size_t BufSiz = 0>
class deque {
public:
typedef _deque_iterator<T, T&, T*, BufSiz> iterator;
typedef reverse_iterator<iterator> reverse_iterator;
iterator begin() { return start; }
iterator end() { return finish; }
reverse_iterator rbegin() { return reverse_iterator(finish); }
reverse_iterator rend() { return reverse_iterator(start); }
};
- 单向容器(如
slist
)不能使用逆向迭代器。 stack
、queue
、priority_queue
不提供begin()
/end()
,因此没有rbegin()
/rend()
。
使用 reverse iterator 的示例
假设有一个 deque<int> id
,内容如下:
1
2
3
4
5
deque<int> id = {32, 26, 99, 1, 0, 1, 2, 3, 4, 0, 1, 2, 5, 3};
cout << *(id.begin()) << endl; // 正向迭代器指向第一个元素
cout << *(id.rbegin()) << endl; // 逆向迭代器指向最后一个元素
cout << *(id.end()) << endl; // end() 超出范围,危险!不要解引用
cout << *(id.rend()) << endl; // rend() 超出范围,危险!不要解引用
从普通迭代器转换到逆向迭代器
1
2
3
4
5
deque<int>::iterator ite = find(id.begin(), id.end(), 99);
reverse_iterator<deque<int>::iterator> rite(ite);
cout << *ite << endl; // 正向迭代器,输出 99
cout << *rite << endl; // 逆向迭代器,输出 26
为什么 *ite
和 *rite
取到不同元素?
这是因为 STL 的迭代器区间遵循前闭后开区间 [begin, end)
:
- 逆向迭代器虽然底层指向同一个地址,但逻辑位置被反转。
rbegin()
对应end()
的前一个位置,rend()
对应begin()
的前一个位置。
因此使用逆向迭代器时,算法处理顺序是“尾到头”,但迭代器的实际地址不变。
源码
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
// reverse_iterator 是一个迭代器适配器(iterator adapter),
// 用来将正向迭代器逆转,使前进方向变为后退,后退变为前进
template <class Iterator>
class reverse_iterator
{
protected:
Iterator current; // 保存对应的正向迭代器(base iterator)
public:
// 逆向迭代器的五种关联型别(associated types),与正向迭代器一致
typedef typename Iterator::iterator_category iterator_category;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
typedef Iterator iterator_type; // 正向迭代器类型
typedef reverse_iterator<Iterator> self; // 自身类型(逆向迭代器)
public:
reverse_iterator() {} // 默认构造函数
// 将 reverse_iterator 与某个正向迭代器 x 绑定
explicit reverse_iterator(iterator_type x) : current(x) {}
// 拷贝构造
reverse_iterator(const self& x) : current(x.current) {}
// 获取对应的正向迭代器
iterator_type base() const { return current; }
// 取值操作符:返回逆向迭代器当前位置对应的元素
// 关键点:返回 base 前一个元素,保证逆序遍历与前闭后开区间一致
reference operator*() const {
Iterator tmp = current;
return *--tmp; // 先退一格再解引用
}
// -> 操作符,返回元素地址
pointer operator->() const { return &(operator*()); }
// 前进操作(++)被逆转为后退(--)
self& operator++() {
--current; // 正向迭代器向前 = 逆向迭代器向后
return *this;
}
self operator++(int) {
self tmp = *this;
--current;
return tmp;
}
// 后退操作(--)被逆转为前进(++)
self& operator--() {
++current; // 正向迭代器向后 = 逆向迭代器向前
return *this;
}
self operator--(int) {
self tmp = *this;
++current;
return tmp;
}
// 支持偏移操作,前进/后退方向完全逆转
self operator+(difference_type n) const { return self(current - n); }
self& operator+=(difference_type n) { current -= n; return *this; }
self operator-(difference_type n) const { return self(current + n); }
self& operator-=(difference_type n) { current += n; return *this; }
// 支持下标操作符
// 注意:第一个 * 会调用 reverse_iterator::operator*()
reference operator[](difference_type n) const { return *(*this + n); }
};
假设 deque<int> id = {1,0,1,2,3,4,0,1,2,5,3};
,当前状态如下:
1
deque<int>::reverse_iterator rite2(id.end()); // 指向最后一个元素的“反向起点”
各操作解释:
表达式 | 含义 | 输出 |
---|---|---|
*(rite2) | 反向迭代器指向的元素(rbegin() 对应 end() 前一个元素) | 3 |
*(+++++rite2) | 反向迭代器前进 5 个位置后解引用 | 1 |
*(--rite2) | 反向迭代器后退 1 个位置后解引用 | 2 |
*(rite2.base()) | 转换为正向迭代器后解引用 | 5 |
rite2[3] | 反向迭代器向前偏移 3 个位置后访问元素 | 4 |
reverse_iterator
的移动方向与正向迭代器相反:++
向前移动时实际上访问容器中前一个元素。base()
返回的是对应的正向迭代器,指向reverse_iterator
的当前元素的下一个元素(所以解引用base()
得到的是下一个元素)。operator[]
也是以反向逻辑进行偏移计算的。
流迭代器(I/O Stream Iterators)
Stream iterators 可以把迭代器绑定到一个 流(stream)对象 上:
- 绑定到
istream
(如std::cin
)的叫 istream_iterator,具有输入能力。 - 绑定到
ostream
(如std::cout
)的叫 ostream_iterator,具有输出能力。
内部原理:迭代器维护一个流对象,当客户端使用 operator++
时,会调用流的输入/输出操作。 特点:istream_iterator
是 输入迭代器(Input Iterator),不支持 operator--
。
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
// 这是一个 Input Iterator,用于从某个 basic_istream 对象执行格式化输入操作。
// 注意:此版本为旧 HP 规格,不完全符合 C++ 标准接口。
// template 参数说明:
// T : 迭代器读取的数据类型
// Distance : 用于计算迭代器之间距离,默认 ptrdiff_t
template <class T, class Distance = ptrdiff_t>
class istream_iterator {
// 定义友元函数,用于比较两个迭代器是否相等
// 在 <stl_config.h> 中,__STL_NULL_TMPL_ARGS 被定义为 <>
friend bool operator== __STL_NULL_TMPL_ARGS (const istream_iterator<T, Distance>&, const istream_iterator<T, Distance>&);
protected:
istream* stream; // 内部维护的流对象指针
T value; // 存储当前读取的值
bool end_marker; // 标记是否到达流末尾
// 内部函数,用于从流中读取数据
void read() {
end_marker = (*stream) ? true : false; // 流可用时标记为 true
if (end_marker) *stream >> value; // 关键:执行输入操作
// 输入后,流状态可能改变,如果流不可读,则更新 end_marker
end_marker = (*stream) ? true : false;
}
public:
// 迭代器类型定义(符合 STL 类型要求)
typedef input_iterator_tag iterator_category; // 输入迭代器
typedef T value_type; // 元素类型
typedef Distance difference_type; // 迭代器差值类型
typedef const T* pointer; // 指针类型
typedef const T& reference; // 引用类型
// 构造函数:默认绑定 cin,end_marker 为 false
istream_iterator() : stream(&cin), end_marker(false) {}
// 构造函数:绑定指定的 istream 对象,并立即读取第一个元素
istream_iterator(istream& s) : stream(&s) { read(); }
// 注意使用方法:
// istream_iterator<int> eos; // end_marker = false, 表示流末尾
// istream_iterator<int> initer(cin); // 会调用 read(),程序会等待输入
// 迭代器取值操作
reference operator*() const { return value; }
pointer operator->() const { return &(operator*()); }
// 前进操作:迭代器前进一个位置,意味着读取下一条数据
istream_iterator<T, Distance>& operator++() {
read(); // 读取下一个元素
return *this;
}
istream_iterator<T, Distance> operator++(int) {
istream_iterator<T, Distance> tmp = *this; // 保留当前状态
read(); // 读取下一个元素
return tmp; // 返回旧值
}
};
- 源代码表明,一旦定义并绑定
istream_iterator
,程序会立即在read()
中等待输入。这通常不是预期行为,因此应 仅在必要时才创建istream_iterator
,这是良好的 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
26
27
28
29
30
31
32
33
34
35
36
37
// 这是一个输出迭代器(output iterator),能够将对象格式化输出到某个 basic_ostream 上
// 注意:此版本为旧 HP 规格,未完全符合标准接口
// ostream_iterator<T, charT, traits>
// 一般使用时只需要第一个模板参数 T,本版本仍适用
// SGI STL 3.3 已实现符合标准接口的版本,本版可读性较高
template <class T>
class ostream_iterator {
protected:
ostream* stream; // 底层输出流对象指针
const char* string; // 每次输出后的间隔符,可为 nullptr
public:
// 输出迭代器的类型定义
typedef output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
// 构造函数:绑定到某个 ostream,对应输出流 s
ostream_iterator(ostream& s) : stream(&s), string(0) {}
// 构造函数:绑定到 ostream,并指定每次输出后的间隔符 c
ostream_iterator(ostream& s, const char* c) : stream(&s), string(c) {}
// 对迭代器赋值操作,相当于向流中输出一个值
ostream_iterator<T>& operator=(const T& value) {
*stream << value; // 输出值
if (string) *stream << string; // 输出间隔符(如果存在)
return *this;
}
// 以下三个操作符对输出迭代器无实际作用,仅返回自身
ostream_iterator<T>& operator*() { return *this; }
ostream_iterator<T>& operator++() { return *this; }
ostream_iterator<T>& operator++(int) { return *this; }
};
仿函数配接器(Functor / Function Adapters)
在理解了模板、容器、算法、仿函数和迭代器之后,配接器(adapter)也可以理解为一种 class template,它“修饰”其他对象,使其具备新功能:
- 容器配接器:内部持有一个容器成员,例如
stack
内部持有deque
。 - 迭代器配接器:内部持有一个迭代器成员,例如
reverse_iterator
内部持有一个正向迭代器。 - 流迭代器:内部持有指向 stream 的指针。
- 插入迭代器:内部持有指向容器的指针,并通过它获取迭代器。
函数配接器(function adapter)的工作原理类似: 它内部持有一个“可配接的仿函数”副本,当调用这个 adapter 时,它可以在参数和返回值上进行操作(如绑定参数、否定结果、组合函数等),并将处理后的结果传给 STL 算法。例如,count_if
搭配 bind2nd(less<int>(), 12)
时,控制权就落到 adapter 手上,从而对函数调用进行修饰和控制。
核心思想:配接器通过内部保存被修饰对象的副本,实现了对调用的“事先准备”,在真正使用时才执行操作。
辅助函数(helperfunction) | 实际产生的配接器对象形式 | 内藏成员的型式 |
---|---|---|
bind1st(const Op& op, const T& x); | binder1st<Op>(op, arg1_type(x)) | Op(二元仿函数) |
bind2nd(const Op& op, const T& x); | binder2nd<Op>(op, arg2_type(x)) | Op(二元仿函数) |
not1(const Pred& pred); | unary_negate<Pred>(pred) | Pred(返回布尔值的仿函数) |
not2(const Pred& pred); | binary_negate<Pred>(pred) | Pred(返回布尔值的仿函数) |
compose1(const Op1& op1, const Op2& op2); | unary_compose<Op1, Op2>(op1, op2) | Op1, Op2 |
compose2(const Op1& op1, const Op2& op2, const Op3& op3); | binary_compose<Op1, Op2, Op3>(op1, op2, op3) | Op1, Op2, Op3 |
ptr_fun(Result(*fp)(Arg)); | pointer_to_unary_function<Arg, Result>(f) | Result(*fp)(Arg) |
ptr_fun(Result(*fp)(Arg1, Arg2)); | pointer_to_binary_function<Arg1, Arg2, Result>(f) | Result(*fp)(Arg1, Arg2) |
mem_fun(S (T::*f)()); | mem_fun_t<S,T>(f) | S (T::*f)() |
mem_fun(S (T::*f)() const); | const_mem_fun_t<S,T>(f) | S (T::*f)() const |
mem_fun_ref(S (T::*f)()); | mem_fun_ref_t<S,T>(f) | S (T::*f)() |
mem_fun_ref(S (T::*f)() const); | const_mem_fun_ref_t<S,T>(f) | S (T::*f)() const |
mem_fun1(S (T::*f)(A)); | mem_fun1_t<S,T,A>(f) | S (T::*f)(A) |
mem_fun1(S (T::*f)(A) const); | const_mem_fun1_t<S,T,A>(f) | S (T::*f)(A) const |
mem_fun1_ref(S (T::*f)(A)); | mem_fun1_ref_t<S,T,A>(f) | S (T::*f)(A) |
mem_fun1_ref(S (T::*f)(A) const); | const_mem_fun1_ref_t<S,T,A>(f) | S (T::*f)(A) const |
- compose1 和 compose2 不在 C++ Standard 规范之内
- 最后四个辅助函数在 C++ Standard 内已去除名称中的 ‘1’
示例:count_if
与 bind2nd
使用
示例函数:count_if
1
2
3
4
5
6
template <class InputIterator, class Predicate, class Size>
void count_if(InputIterator first, InputIterator last, Predicate pred, Size& n) {
for (; first != last; ++first) // 遍历区间 [first, last)
if (pred(*first)) // 调用 pred 对当前元素进行判断
++n; // 若返回 true,则计数器累加
}
- 统计序列中满足条件的元素个数。
- 注意:
Predicate
可以是普通函数、仿函数,或经过 function adapter 修饰的对象。
示例适配器:binder2nd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <class Operation>
class binder2nd : public unary_function</*first_type, result_type*/> {
protected:
Operation op; // 内部保存原始二元仿函数
typename Operation::second_argument_type value; // 第二个参数固定值
public:
binder2nd(const Operation& x, const typename Operation::second_argument_type& y)
: op(x), value(y) {}
typename Operation::result_type
operator()(const typename Operation::first_argument_type& x) const {
return op(x, value); // 调用原始二元仿函数,将 value 绑定为第二个参数
}
};
- 将一个二元仿函数(如
less<int>()
)的第二个参数固定,返回一个一元仿函数。 - 让原本需要两个参数的函数,适用于只提供一个参数的算法(如
count_if
)。
使用示例
1
2
3
4
5
6
7
std::vector<int> iv = {10, 5, 12, 20, 8};
int n = 0;
// 将 less<int>() 的第二个参数绑定为 12,生成一元仿函数
count_if(iv.begin(), iv.end(), bind2nd(less<int>(), 12), n);
std::cout << "小于 12 的元素个数: " << n << std::endl;
过程:
bind2nd(less<int>(), 12)
生成一个binder2nd<less<int>>
对象。count_if
接受该对象作为pred
参数。对每个元素
x
,pred(x)
实际上执行less<int>()(x, 12)
。返回
true
时计数器增加。
对返回值进行逻辑否定:notl1
,not2
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
//===============================
// 逻辑非(unary_negate)适配器
//===============================
/*
* 将一个一元可适配谓词(Adaptable Predicate)的返回值取反。
* 例如,原谓词 pred(x) 返回 true,则 unary_negate(pred)(x) 返回 false。
*/
template <class Predicate>
class unary_negate
: public unary_function<typename Predicate::argument_type, bool> // 继承 STL 标准一元仿函数接口
{
protected:
Predicate pred; // 内部保存原始谓词对象
public:
explicit unary_negate(const Predicate& x) : pred(x) {} // 构造函数:保存传入的谓词
bool operator()(const typename Predicate::argument_type& x) const {
return !pred(x); // 对原谓词的返回值取逻辑非
}
};
// 辅助函数,方便创建 unary_negate 对象
template <class Predicate>
inline unary_negate<Predicate> not1(const Predicate& pred) {
return unary_negate<Predicate>(pred);
}
//===============================
// 逻辑非(binary_negate)适配器
//===============================
/*
* 将一个二元可适配谓词(Adaptable Binary Predicate)的返回值取反。
* 例如,原谓词 pred(x, y) 返回 true,则 binary_negate(pred)(x, y) 返回 false。
*/
template <class Predicate>
class binary_negate
: public binary_function<
typename Predicate::first_argument_type,
typename Predicate::second_argument_type,
bool> // 继承 STL 标准二元仿函数接口
{
protected:
Predicate pred; // 内部保存原始谓词对象
public:
explicit binary_negate(const Predicate& x) : pred(x) {} // 构造函数:保存传入的谓词
bool operator()(const typename Predicate::first_argument_type& x,
const typename Predicate::second_argument_type& y) const {
return !pred(x, y); // 对原谓词的返回值取逻辑非
}
};
// 辅助函数,方便创建 binary_negate 对象
template <class Predicate>
inline binary_negate<Predicate> not2(const Predicate& pred) {
return binary_negate<Predicate>(pred);
}
unary_negate
与binary_negate
都是 function adapters,它们内部保存原谓词对象。- 调用
operator()
时,会先调用原谓词计算结果,再取逻辑非。 not1(pred)
和not2(pred)
是创建适配器的简便方法,避免显式写模板类。
对参数进行绑定:bind1st
, bind2nd
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
//=====================================================
// binder1st:将二元可适配函数(BinFunc)转换为一元函数(Unary Function)
//=====================================================
/*
* 将一个二元函数的第一个参数绑定为固定值,从而产生一个一元函数。
* 例如,原函数 op(a, b),绑定 a=value 后,得到一元函数 f(b)=op(value, b)。
*/
template <class Operation>
class binder1st
: public unary_function<
typename Operation::second_argument_type, // 一元函数参数类型
typename Operation::result_type> // 一元函数返回类型
{
protected:
Operation op; // 保存原二元函数对象
typename Operation::first_argument_type value; // 固定绑定的第一个参数
public:
// 构造函数:绑定原函数与第一个参数
binder1st(const Operation& x,
const typename Operation::first_argument_type& y)
: op(x), value(y) {}
// 重载 operator():实际调用原二元函数,将第一个参数绑定为 value
typename Operation::result_type
operator()(const typename Operation::second_argument_type& x) const {
return op(value, x);
}
};
// 辅助函数,方便创建 binder1st 对象
template <class Operation, class T>
inline bind1st<Operation> bind1st(const Operation& op, const T& x) {
typedef typename Operation::first_argument_type arg1_type;
return binder1st<Operation>(op, arg1_type(x));
// 将 x 转型为原函数第一个参数类型
}
//=====================================================
// binder2nd:将二元可适配函数(BinFunc)转换为一元函数(Unary Function)
//=====================================================
/*
* 将一个二元函数的第二个参数绑定为固定值,从而产生一个一元函数。
* 例如,原函数 op(a, b),绑定 b=value 后,得到一元函数 f(a)=op(a, value)。
*/
template <class Operation>
class binder2nd
: public unary_function<
typename Operation::first_argument_type, // 一元函数参数类型
typename Operation::result_type> // 一元函数返回类型
{
protected:
Operation op; // 保存原二元函数对象
typename Operation::second_argument_type value; // 固定绑定的第二个参数
public:
// 构造函数:绑定原函数与第二个参数
binder2nd(const Operation& x,
const typename Operation::second_argument_type& y)
: op(x), value(y) {}
// 重载 operator():实际调用原二元函数,将第二个参数绑定为 value
typename Operation::result_type
operator()(const typename Operation::first_argument_type& x) const {
return op(x, value);
}
};
// 辅助函数,方便创建 binder2nd 对象
template <class Operation, class T>
inline bind2nd<Operation> bind2nd(const Operation& op, const T& x) {
typedef typename Operation::second_argument_type arg2_type;
return binder2nd<Operation>(op, arg2_type(x));
// 将 x 转型为原函数第二个参数类型
}
binder1st
与binder2nd
都是 function adapters,用于固定二元函数的一个参数,生成一元函数。- 这样可以与 STL 算法(如
count_if
,find_if
等)兼容,因为它们通常接受一元谓词。 - 辅助函数
bind1st
和bind2nd
简化了对象创建过程,自动完成类型转换。
用于函数合成: compose1
, compose2
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
//=====================================================
// unary_compose:一元函数的组合 h(x) = f(g(x))
//=====================================================
/*
* 已知两个 Adaptable Unary Function f() 和 g(),
* unary_compose 生成一个新的函数 h(x) = f(g(x))。
*/
template <class Operation1, class Operation2>
class unary_compose
: public unary_function<
typename Operation2::argument_type, // 输入类型
typename Operation1::result_type> // 输出类型
{
protected:
Operation1 op1; // 内部成员,保存外层函数 f
Operation2 op2; // 内部成员,保存内层函数 g
public:
// 构造函数:记录两个函数对象
unary_compose(const Operation1& x, const Operation2& y)
: op1(x), op2(y) {}
// 重载 operator():调用 g(x),再将结果传给 f
typename Operation1::result_type
operator()(const typename Operation2::argument_type& x) const {
return op1(op2(x)); // 函数合成
}
};
// 辅助函数,使使用更方便
template <class Operation1, class Operation2>
inline unary_compose<Operation1, Operation2>
compose1(const Operation1& op1, const Operation2& op2) {
return unary_compose<Operation1, Operation2>(op1, op2);
}
//=====================================================
// binary_compose:二元函数与两一元函数组合 h(x) = f(g1(x), g2(x))
//=====================================================
/*
* 已知一个 Adaptable Binary Function f 和两个 Adaptable Unary Function g1, g2,
* binary_compose 生成一个新函数 h(x) = f(g1(x), g2(x))。
*/
template <class Operation1, class Operation2, class Operation3>
class binary_compose
: public unary_function<
typename Operation2::argument_type, // 输入类型
typename Operation1::result_type> // 输出类型
{
protected:
Operation1 op1; // 内部成员,保存二元函数 f
Operation2 op2; // 内部成员,保存第一一元函数 g1
Operation3 op3; // 内部成员,保存第二一元函数 g2
public:
// 构造函数:记录三个函数对象
binary_compose(const Operation1& x, const Operation2& y, const Operation3& z)
: op1(x), op2(y), op3(z) {}
// 重载 operator():调用 g1(x), g2(x),再传入 f
typename Operation1::result_type
operator()(const typename Operation2::argument_type& x) const {
return op1(op2(x), op3(x)); // 函数合成
}
};
// 辅助函数,使使用更方便
template <class Operation1, class Operation2, class Operation3>
inline binary_compose<Operation1, Operation2, Operation3>
compose2(const Operation1& op1,
const Operation2& op2,
const Operation3& op3) {
return binary_compose<Operation1, Operation2, Operation3>(op1, op2, op3);
}
unary_compose
:组合两个一元函数,形成新的单参数函数。binary_compose
:将二元函数与两一元函数组合,形成新的单参数函数。- 辅助函数
compose1
和compose2
简化对象创建,直接生成组合函数,方便传入 STL 算法或其他上下文使用。
用于函数指针:ptr_fun
这种配接器能把普通函数包装成仿函数,方便与 STL 算法和其他配接器配合使用。否则,普通函数缺乏配接能力,无法无缝衔接到泛型算法体系中。
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
//=====================================================
// pointer_to_unary_function:将一元函数指针封装为仿函数
//=====================================================
/*
* 作用:
* 将一个普通的一元函数指针包装成仿函数对象。
* 使用该对象时,会调用内部存储的函数指针。
*/
template <class Arg, class Result>
class pointer_to_unary_function : public unary_function<Arg, Result>
{
protected:
Result (*ptr)(Arg); // 内部成员:函数指针
public:
pointer_to_unary_function() {}
// 构造函数:记录函数指针
explicit pointer_to_unary_function(Result (*x)(Arg)) : ptr(x) {}
// 调用运算符:通过函数指针执行函数
Result operator()(Arg x) const {
return ptr(x);
}
};
// 辅助函数:方便创建 pointer_to_unary_function 对象
template <class Arg, class Result>
inline pointer_to_unary_function<Arg, Result>
ptr_fun(Result (*x)(Arg)) {
return pointer_to_unary_function<Arg, Result>(x);
}
//=====================================================
// pointer_to_binary_function:将二元函数指针封装为仿函数
//=====================================================
/*
* 作用:
* 将一个普通的二元函数指针包装成仿函数对象。
* 使用该对象时,会调用内部存储的函数指针。
*/
template <class Arg1, class Arg2, class Result>
class pointer_to_binary_function : public binary_function<Arg1, Arg2, Result>
{
protected:
Result (*ptr)(Arg1, Arg2); // 内部成员:函数指针
public:
pointer_to_binary_function() {}
// 构造函数:记录函数指针
explicit pointer_to_binary_function(Result (*x)(Arg1, Arg2)) : ptr(x) {}
// 调用运算符:通过函数指针执行函数
Result operator()(Arg1 x, Arg2 y) const {
return ptr(x, y);
}
};
// 辅助函数:方便创建 pointer_to_binary_function 对象
template <class Arg1, class Arg2, class Result>
inline pointer_to_binary_function<Arg1, Arg2, Result>
ptr_fun(Result (*x)(Arg1, Arg2)) {
return pointer_to_binary_function<Arg1, Arg2, Result>(x);
}
pointer_to_unary_function
:把一元普通函数变成仿函数对象,可用于 STL 算法。pointer_to_binary_function
:把二元普通函数变成仿函数对象,可用于 STL 算法。- 两者都提供了 辅助函数
ptr_fun()
,简化对象创建,直接返回封装后的仿函数对象。
用于成员函数指针:mem_fun
, mem_fun_ref
这种配接器可以把成员函数(member function)当作仿函数使用,使得成员函数能与各种泛型算法结合。当容器元素类型是 X&
或 X*
,且成员函数为虚函数(virtual),泛型算法可以实现多态调用(polymorphic call)。这是泛型编程(genericity)与多态(polymorphism)结合的一个典型应用。
下面例子展示了如何使用成员函数配接器 mem_fun
来实现多态调用:
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
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
class Shape {
public:
virtual void display() = 0;
};
class Rect : public Shape {
public:
void display() override { cout << "Rect "; }
};
class Circle : public Shape {
public:
void display() override { cout << "Circle "; }
};
class Square : public Rect {
public:
void display() override { cout << "Square "; }
};
int main() {
vector<Shape*> V;
V.push_back(new Rect);
V.push_back(new Circle);
V.push_back(new Square);
V.push_back(new Circle);
V.push_back(new Rect);
// 多态调用
for (int i = 0; i < V.size(); ++i)
V[i]->display();
cout << endl; // 输出: Rect Circle Square Circle Rect
// 使用成员函数配接器
for_each(V.begin(), V.end(), mem_fun(&Shape::display));
cout << endl; // 输出: Rect Circle Square Circle Rect
}
- 不能直接传成员函数指针给
for_each
,必须使用mem_fun
修饰。 - 多态对指针或引用有效,但 STL 容器只支持“值语意”,不支持存放引用,例如
vector<Shape&>
无法通过编译。
无参数成员函数
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
// =========================================================
// 成员函数配接器 (Adapters for member functions)
// 用途:把成员函数 (member functions) 转换成可调用的仿函数对象
// 分类:
// (1) 无参数 / 有一个参数
// (2) 通过 pointer 调用 / 通过 reference 调用
// (3) const 成员函数 / 非 const 成员函数
// 最终使用时,可以直接用 mem_fun / mem_fun_ref 等辅助函数
// =========================================================
// (无参数 + pointer 调用 + 非 const)
template <class S, class T>
class mem_fun_t : public unary_function<T*, S> {
public:
explicit mem_fun_t(S (T::*pf)()) : f(pf) {} // 保存成员函数指针
S operator()(T* p) const { return (p->*f)(); } // 调用成员函数
private:
S (T::*f)(); // 内部成员:成员函数指针
};
// (无参数 + pointer 调用 + const)
template <class S, class T>
class const_mem_fun_t : public unary_function<const T*, S> {
public:
explicit const_mem_fun_t(S (T::*pf)() const) : f(pf) {}
S operator()(const T* p) const { return (p->*f)(); }
private:
S (T::*f)() const; // 指向 const 成员函数的指针
};
// (无参数 + reference 调用 + 非 const)
template <class S, class T>
class mem_fun_ref_t : public unary_function<T, S> {
public:
explicit mem_fun_ref_t(S (T::*pf)()) : f(pf) {}
S operator()(T& r) const { return (r.*f)(); }
private:
S (T::*f)();
};
// (无参数 + reference 调用 + const)
template <class S, class T>
class const_mem_fun_ref_t : public unary_function<T, S> {
public:
explicit const_mem_fun_ref_t(S (T::*pf)() const) : f(pf) {}
S operator()(const T& r) const { return (r.*f)(); }
private:
S (T::*f)() const;
};
有一个参数的成员函数
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
// (一个参数 + pointer 调用 + 非 const)
template <class S, class T, class A>
class mem_fun1_t : public binary_function<T*, A, S> {
public:
explicit mem_fun1_t(S (T::*pf)(A)) : f(pf) {}
S operator()(T* p, A x) const { return (p->*f)(x); }
private:
S (T::*f)(A);
};
// (一个参数 + pointer 调用 + const)
template <class S, class T, class A>
class const_mem_fun1_t : public binary_function<const T*, A, S> {
public:
explicit const_mem_fun1_t(S (T::*pf)(A) const) : f(pf) {}
S operator()(const T* p, A x) const { return (p->*f)(x); }
private:
S (T::*f)(A) const;
};
// (一个参数 + reference 调用 + 非 const)
template <class S, class T, class A>
class mem_fun1_ref_t : public binary_function<T, A, S> {
public:
explicit mem_fun1_ref_t(S (T::*pf)(A)) : f(pf) {}
S operator()(T& r, A x) const { return (r.*f)(x); }
private:
S (T::*f)(A);
};
// (一个参数 + reference 调用 + const)
template <class S, class T, class A>
class const_mem_fun1_ref_t : public binary_function<T, A, S> {
public:
explicit const_mem_fun1_ref_t(S (T::*pf)(A) const) : f(pf) {}
S operator()(const T& r, A x) const { return (r.*f)(x); }
private:
S (T::*f)(A) const;
};
辅助函数(工厂函数)
这些函数帮你快速生成正确的适配器,无需自己去写模板参数。
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
// ------------------ 无参数 ------------------
template <class S, class T>
inline mem_fun_t<S,T> mem_fun(S (T::*f)()) {
return mem_fun_t<S,T>(f);
}
template <class S, class T>
inline const_mem_fun_t<S,T> mem_fun(S (T::*f)() const) {
return const_mem_fun_t<S,T>(f);
}
template <class S, class T>
inline mem_fun_ref_t<S,T> mem_fun_ref(S (T::*f)()) {
return mem_fun_ref_t<S,T>(f);
}
template <class S, class T>
inline const_mem_fun_ref_t<S,T> mem_fun_ref(S (T::*f)() const) {
return const_mem_fun_ref_t<S,T>(f);
}
// ------------------ 有一个参数 ------------------
template <class S, class T, class A>
inline mem_fun1_t<S,T,A> mem_fun(S (T::*f)(A)) {
return mem_fun1_t<S,T,A>(f);
}
template <class S, class T, class A>
inline const_mem_fun1_t<S,T,A> mem_fun(S (T::*f)(A) const) {
return const_mem_fun1_t<S,T,A>(f);
}
template <class S, class T, class A>
inline mem_fun1_ref_t<S,T,A> mem_fun_ref(S (T::*f)(A)) {
return mem_fun1_ref_t<S,T,A>(f);
}
template <class S, class T, class A>
inline const_mem_fun1_ref_t<S,T,A> mem_fun_ref(S (T::*f)(A) const) {
return const_mem_fun1_ref_t<S,T,A>(f);
}
mem_fun
/mem_fun_ref
就是工厂函数,帮你把 成员函数指针 转换为 仿函数对象。mem_fun
适用于容器存 指针 的情况,例如vector<Shape*>
。mem_fun_ref
适用于容器存 对象 的情况,例如vector<Shape>
。- 分为 无参数/有一个参数、const/非 const 两大类,总共 8 种。