文章

配接器

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_iteratorpush_back()
    • front_insert_iteratorpush_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) 将第二个参数固定为 12
  • not1(...) 取逻辑否定 → “不小于 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_functionbinary_function
  • 函数或成员函数必须通过配接器处理后,才能与 STL 算法无缝结合。
  • 不符合接口要求的函数无法直接使用(如 for_each)。

容器配接器(Container Adapters)

容器配接器是一种作用于已有容器之上的 接口封装器,它隐藏了底层容器的部分接口,只暴露符合特定数据结构原则的操作。

STL 中典型的容器配接器包括 stackqueue

stack

  • 底层容器:默认由 deque 构成(也可以使用其他序列容器)。
  • 模板定义
1
2
3
4
5
6
template <class T, class Sequence = deque<T>>
class stack {
protected:
    Sequence c;  // 底层容器
    // ...
};
  • 特点
    1. 只开放符合 栈(LIFO) 原则的接口,如:
      • push():压入元素
      • pop():弹出元素
      • top():访问栈顶元素
      • empty() / size()
    2. 隐藏底层 deque 的其他接口,如随机访问和迭代器。
    3. 因此,stack 是一个 容器配接器,通过封装底层容器实现栈行为。

queue

  • 底层容器:默认由 deque 构成(也可以使用其他序列容器)。
  • 模板定义
1
2
3
4
5
6
template <class T, class Sequence = deque<T>>
class queue {
protected:
    Sequence c;  // 底层容器
    // ...
};
  • 特点
    1. 只开放符合 队列(FIFO) 原则的接口,如:
      • push():入队
      • pop():出队
      • front() / back():访问队头和队尾
      • empty() / size()
    2. 隐藏底层 deque 的其他接口。
    3. 因此,queue 也是一个 容器配接器,通过封装底层容器实现队列行为。

迭代器配接器(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();   // 指向第一个元素的前一个位置

示例:vectorlistdeque

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)不能使用逆向迭代器。
  • stackqueuepriority_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_ifbind2nd 使用

示例函数: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 参数。

    • 对每个元素 xpred(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_negatebinary_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 转型为原函数第二个参数类型
}
  • binder1stbinder2nd 都是 function adapters,用于固定二元函数的一个参数,生成一元函数。
  • 这样可以与 STL 算法(如 count_if, find_if 等)兼容,因为它们通常接受一元谓词。
  • 辅助函数 bind1stbind2nd 简化了对象创建过程,自动完成类型转换。

用于函数合成: 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:将二元函数与两一元函数组合,形成新的单参数函数。
  • 辅助函数 compose1compose2 简化对象创建,直接生成组合函数,方便传入 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 种。
本文由作者按照 CC BY 4.0 进行授权