适配器就是Interface(接口),对容器、迭代器和算法进行包装,但其实质还是容器、迭代器和算法,只是不依赖于具体的标准容器、迭代器和算法类型,容器适配器可以理解为容器的模板,迭代器适配器可理解为迭代器的模板,算法适配器可理解为算法的模板。
容器适配器(Container Adapters)是对现有容器的封装,提供特定的接口来实现特殊的数据结构。STL提供了三种主要的容器适配器:
LIFO(Last In, First Out)后进先出
只能从顶部插入和删除元素
不提供迭代器
xxxxxxxxxx
231
2
3
4int main() {
5 std::stack<int> s;
6
7 // 入栈
8 s.push(1);
9 s.push(2);
10 s.push(3);
11
12 // 访问栈顶元素
13 std::cout << "栈顶元素: " << s.top() << std::endl; // 输出: 3
14
15 // 出栈
16 while (!s.empty()) {
17 std::cout << s.top() << " ";
18 s.pop();
19 }
20 // 输出: 3 2 1
21
22 return 0;
23}
xxxxxxxxxx
151// 构造和赋值
2std::stack<int> s1; // 默认构造
3std::stack<int> s2(s1); // 拷贝构造
4
5// 元素访问
6int& top_element = s.top(); // 访问栈顶元素
7
8// 容量
9bool is_empty = s.empty(); // 检查是否为空
10size_t size = s.size(); // 获取元素个数
11
12// 修改器
13s.push(42); // 入栈
14s.pop(); // 出栈(不返回值)
15s.emplace(args...); // 原地构造并入栈
xxxxxxxxxx
61// 默认使用deque作为底层容器
2std::stack<int> s1; // 等价于 std::stack<int, std::deque<int>>
3
4// 可以指定其他容器
5std::stack<int, std::vector<int>> s2; // 使用vector
6std::stack<int, std::list<int>> s3; // 使用list
xxxxxxxxxx
251
2
3
4bool isValidParentheses(const std::string& s) {
5 std::stack<char> st;
6
7 for (char c : s) {
8 if (c == '(' || c == '[' || c == '{') {
9 st.push(c);
10 } else {
11 if (st.empty()) return false;
12
13 char top = st.top();
14 st.pop();
15
16 if ((c == ')' && top != '(') ||
17 (c == ']' && top != '[') ||
18 (c == '}' && top != '{')) {
19 return false;
20 }
21 }
22 }
23
24 return st.empty();
25}
FIFO(First In, First Out)先进先出
从后端插入,从前端删除
不提供迭代器
xxxxxxxxxx
241
2
3
4int main() {
5 std::queue<int> q;
6
7 // 入队
8 q.push(1);
9 q.push(2);
10 q.push(3);
11
12 // 访问队首和队尾
13 std::cout << "队首: " << q.front() << std::endl; // 输出: 1
14 std::cout << "队尾: " << q.back() << std::endl; // 输出: 3
15
16 // 出队
17 while (!q.empty()) {
18 std::cout << q.front() << " ";
19 q.pop();
20 }
21 // 输出: 1 2 3
22
23 return 0;
24}
xxxxxxxxxx
121// 元素访问
2int& front_element = q.front(); // 访问队首元素
3int& back_element = q.back(); // 访问队尾元素
4
5// 容量
6bool is_empty = q.empty(); // 检查是否为空
7size_t size = q.size(); // 获取元素个数
8
9// 修改器
10q.push(42); // 入队
11q.pop(); // 出队(不返回值)
12q.emplace(args...); // 原地构造并入队
xxxxxxxxxx
61// 默认使用deque作为底层容器
2std::queue<int> q1; // 等价于 std::queue<int, std::deque<int>>
3
4// 可以指定其他容器
5std::queue<int, std::list<int>> q2; // 使用list
6// 注意:不能使用vector,因为vector没有pop_front()方法
xxxxxxxxxx
241
2
3
4void bfs(std::vector<std::vector<int>>& graph, int start) {
5 std::queue<int> q;
6 std::vector<bool> visited(graph.size(), false);
7
8 q.push(start);
9 visited[start] = true;
10
11 while (!q.empty()) {
12 int current = q.front();
13 q.pop();
14
15 std::cout << current << " ";
16
17 for (int neighbor : graph[current]) {
18 if (!visited[neighbor]) {
19 visited[neighbor] = true;
20 q.push(neighbor);
21 }
22 }
23 }
24}
堆结构,默认为最大堆
自动按优先级排序
不提供迭代器
xxxxxxxxxx
251
2
3
4
5int main() {
6 std::priority_queue<int> pq;
7
8 // 插入元素
9 pq.push(3);
10 pq.push(1);
11 pq.push(4);
12 pq.push(2);
13
14 // 访问最大元素
15 std::cout << "最大元素: " << pq.top() << std::endl; // 输出: 4
16
17 // 按优先级输出
18 while (!pq.empty()) {
19 std::cout << pq.top() << " ";
20 pq.pop();
21 }
22 // 输出: 4 3 2 1
23
24 return 0;
25}
xxxxxxxxxx
221// 最小堆
2std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;
3
4// 自定义比较函数
5struct Person {
6 std::string name;
7 int age;
8};
9
10struct PersonComparator {
11 bool operator()(const Person& a, const Person& b) {
12 return a.age < b.age; // 按年龄降序(年龄大的优先级高)
13 }
14};
15
16std::priority_queue<Person, std::vector<Person>, PersonComparator> person_pq;
17
18// 使用lambda表达式
19auto cmp = [](const Person& a, const Person& b) {
20 return a.age < b.age;
21};
22std::priority_queue<Person, std::vector<Person>, decltype(cmp)> person_pq2(cmp);
xxxxxxxxxx
111// 元素访问
2const int& top_element = pq.top(); // 访问优先级最高的元素
3
4// 容量
5bool is_empty = pq.empty(); // 检查是否为空
6size_t size = pq.size(); // 获取元素个数
7
8// 修改器
9pq.push(42); // 插入元素
10pq.pop(); // 删除优先级最高的元素
11pq.emplace(args...); // 原地构造并插入
xxxxxxxxxx
51// 默认使用vector作为底层容器,less<T>作为比较器
2std::priority_queue<int> pq1; // 等价于 std::priority_queue<int, std::vector<int>, std::less<int>>
3
4// 可以指定其他容器和比较器
5std::priority_queue<int, std::deque<int>, std::greater<int>> pq2;
xxxxxxxxxx
401
2
3
4
5struct Edge {
6 int to, weight;
7};
8
9struct State {
10 int node, dist;
11 bool operator>(const State& other) const {
12 return dist > other.dist; // 最小堆
13 }
14};
15
16std::vector<int> dijkstra(const std::vector<std::vector<Edge>>& graph, int start) {
17 int n = graph.size();
18 std::vector<int> dist(n, std::numeric_limits<int>::max());
19 std::priority_queue<State, std::vector<State>, std::greater<State>> pq;
20
21 dist[start] = 0;
22 pq.push({start, 0});
23
24 while (!pq.empty()) {
25 State current = pq.top();
26 pq.pop();
27
28 if (current.dist > dist[current.node]) continue;
29
30 for (const Edge& edge : graph[current.node]) {
31 int new_dist = dist[current.node] + edge.weight;
32 if (new_dist < dist[edge.to]) {
33 dist[edge.to] = new_dist;
34 pq.push({edge.to, new_dist});
35 }
36 }
37 }
38
39 return dist;
40}
共同特点
封装性:隐藏底层容器的复杂性
接口限制:只提供特定操作,确保数据结构的完整性
无迭代器:不提供迭代器访问,保持数据结构的抽象性
底层容器可选:可以选择不同的底层容器实现
底层容器要求
stack:需要支持back()
, push_back()
, pop_back()
queue:需要支持front()
, back()
, push_back()
, pop_front()
priority_queue:需要支持随机访问迭代器,front()
, push_back()
, pop_back()
性能
stack:所有操作都是O(1)
queue:所有操作都是O(1)
priority_queue:插入和删除是O(log n),访问最大元素是O(1)
迭代器适配器(Iterator Adapters)是对现有迭代器的封装,提供特殊的功能和行为。主要包括插入迭代器、反向迭代器、移动迭代器和流迭代器等。
插入迭代器(Insert Iterators)将赋值操作转换为插入操作,主要有三种类型:
在容器尾部插入元素:
xxxxxxxxxx
251
2
3
4
5
6int main() {
7 std::vector<int> vec1 = {1, 2, 3};
8 std::vector<int> vec2;
9
10 // 使用 back_insert_iterator
11 std::back_insert_iterator<std::vector<int>> back_it(vec2);
12 *back_it = 10; // 等价于 vec2.push_back(10)
13 *back_it = 20;
14 *back_it = 30;
15
16 // 使用 back_inserter 辅助函数(更常用)
17 std::copy(vec1.begin(), vec1.end(), std::back_inserter(vec2));
18
19 // 输出: 10 20 30 1 2 3
20 for (int x : vec2) {
21 std::cout << x << " ";
22 }
23
24 return 0;
25}
在容器前端插入元素(仅支持deque、list等):
xxxxxxxxxx
191
2
3
4
5
6int main() {
7 std::deque<int> deq1 = {1, 2, 3};
8 std::deque<int> deq2;
9
10 // 使用 front_inserter
11 std::copy(deq1.begin(), deq1.end(), std::front_inserter(deq2));
12
13 // 输出: 3 2 1 (注意顺序相反)
14 for (int x : deq2) {
15 std::cout << x << " ";
16 }
17
18 return 0;
19}
在指定位置插入元素:
xxxxxxxxxx
201
2
3
4
5
6int main() {
7 std::vector<int> vec1 = {1, 2, 3};
8 std::vector<int> vec2 = {10, 20, 30};
9
10 // 在 vec2 的第二个位置插入 vec1 的所有元素
11 auto it = vec2.begin() + 1;
12 std::copy(vec1.begin(), vec1.end(), std::inserter(vec2, it));
13
14 // 输出: 10 1 2 3 20 30
15 for (int x : vec2) {
16 std::cout << x << " ";
17 }
18
19 return 0;
20}
xxxxxxxxxx
91// 插入迭代器的行为
2std::vector<int> vec;
3auto back_it = std::back_inserter(vec);
4
5// 这些操作都等价于 vec.push_back(value)
6*back_it = 42; // 赋值转换为插入
7++back_it; // 递增操作无效果
8back_it++; // 后置递增无效果
9*back_it++ = 100; // 连续操作
第6行和第9行完全等价,关键在于理解 back_insert_iterator
的内部实现机制。虽然递增操作"无效果",但它们仍然返回有效的迭代器对象,这使得连续操作成为可能。
xxxxxxxxxx
11*back_it++ = 100;
这个表达式的执行顺序:
back_it++
执行后置递增,返回 *this
(迭代器自身的副本)
*
对返回的迭代器解引用,又返回迭代器自身
= 100
调用赋值操作符,执行 container->push_back(100)
关键点:返回的是有效对象
虽然递增操作不改变迭代器的内部状态,但它们都返回有效的迭代器对象:
xxxxxxxxxx
131// 演示各个操作的返回值
2std::vector<int> vec;
3auto back_it = std::back_inserter(vec);
4
5// 每个操作都返回有效的迭代器
6auto it1 = back_it++; // 返回迭代器副本
7auto it2 = ++back_it; // 返回迭代器引用
8auto it3 = *back_it; // 返回迭代器引用
9
10// 所有这些都可以用来插入
11*it1 = 1; // vec.push_back(1)
12*it2 = 2; // vec.push_back(2)
13*it3 = 3; // vec.push_back(3)
"无效果"指的是:
递增操作不会改变迭代器指向的位置(因为插入迭代器没有"位置"概念)
不会影响迭代器的功能性
但不是指:
操作无法执行
返回无效对象
xxxxxxxxxx
231
2
3
4
5int main() {
6 std::vector<int> vec;
7 auto back_it = std::back_inserter(vec);
8
9 // 所有这些操作都有效
10 *back_it = 42; // vec: [42]
11 ++back_it; // "无效果",但 back_it 仍然有效
12 *back_it = 100; // vec: [42, 100]
13
14 // 连续操作也有效
15 *back_it++ = 200; // vec: [42, 100, 200]
16
17 // 验证结果
18 for (int x : vec) {
19 std::cout << x << " "; // 输出: 42 100 200
20 }
21
22 return 0;
23}
虽然递增操作对迭代器状态"无效果",但它们保证返回功能完整的迭代器对象,这是插入迭代器设计的巧妙之处。
既然 *back_it 和 *back_it++ 这两种写法功能完全相同,为什么还需要++?
从功能角度来看,对于 back_insert_iterator
确实不写 ++
也能达到一样的插入效果。但是设计上提供 ++
操作符有重要的原因:
迭代器概念的完整性 插入迭代器必须满足 OutputIterator 的概念要求,而 OutputIterator 要求必须支持:
*it = value
(赋值)
++it
(前置递增)
it++
(后置递增)
与 STL 算法的兼容性,许多 STL 算法都使用 *it++
这种惯用法。std::copy
内部会使用 *result++
的形式,如果 back_inserter
不支持 ++
,这个调用就会失败。
统一的编程接口,提供 ++
让插入迭代器与其他迭代器有统一的使用方式。
反向迭代器(Reverse Iterators)使得容器可以反向遍历:
xxxxxxxxxx
291
2
3
4
5int main() {
6 std::vector<int> vec = {1, 2, 3, 4, 5};
7
8 // 使用反向迭代器
9 std::cout << "正向: ";
10 for (auto it = vec.begin(); it != vec.end(); ++it) {
11 std::cout << *it << " ";
12 }
13 // 输出: 1 2 3 4 5
14
15 std::cout << "\n反向: ";
16 for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
17 std::cout << *it << " ";
18 }
19 // 输出: 5 4 3 2 1
20
21 // 手动创建反向迭代器
22 std::reverse_iterator<std::vector<int>::iterator> rev_it(vec.end());
23 std::cout << "\n手动反向: ";
24 for (; rev_it != std::reverse_iterator<std::vector<int>::iterator>(vec.begin()); ++rev_it) {
25 std::cout << *rev_it << " ";
26 }
27
28 return 0;
29}
C++11引入,用于移动语义:
xxxxxxxxxx
301
2
3
4
5
6
7int main() {
8 std::vector<std::string> source = {"hello", "world", "cpp"};
9 std::vector<std::string> dest;
10
11 std::cout << "移动前 source 大小: " << source.size() << std::endl;
12
13 // 使用移动迭代器
14 std::copy(std::make_move_iterator(source.begin()),
15 std::make_move_iterator(source.end()),
16 std::back_inserter(dest));
17
18 std::cout << "移动后 source 大小: " << source.size() << std::endl;
19 std::cout << "dest 内容: ";
20 for (const auto& s : dest) {
21 std::cout << s << " ";
22 }
23
24 std::cout << "\nsource 内容: ";
25 for (const auto& s : source) {
26 std::cout << "'" << s << "' "; // 可能为空字符串
27 }
28
29 return 0;
30}
xxxxxxxxxx
391
2
3
4
5
6class LargeObject {
7public:
8 std::string data;
9
10 LargeObject(const std::string& s) : data(s) {}
11
12 // 拷贝构造函数
13 LargeObject(const LargeObject& other) : data(other.data) {
14 std::cout << "拷贝构造: " << data << std::endl;
15 }
16
17 // 移动构造函数
18 LargeObject(LargeObject&& other) noexcept : data(std::move(other.data)) {
19 std::cout << "移动构造: " << data << std::endl;
20 }
21};
22
23int main() {
24 std::vector<LargeObject> source;
25 source.emplace_back("object1");
26 source.emplace_back("object2");
27
28 std::vector<LargeObject> dest1, dest2;
29
30 std::cout << "=== 拷贝操作 ===\n";
31 std::copy(source.begin(), source.end(), std::back_inserter(dest1));
32
33 std::cout << "\n=== 移动操作 ===\n";
34 std::copy(std::make_move_iterator(source.begin()),
35 std::make_move_iterator(source.end()),
36 std::back_inserter(dest2));
37
38 return 0;
39}
从输入流读取数据:
xxxxxxxxxx
401
2
3
4
5
6
7int main() {
8 // 从字符串流读取
9 std::istringstream iss("1 2 3 4 5");
10
11 // 创建输入流迭代器
12 std::istream_iterator<int> input_it(iss);
13 std::istream_iterator<int> end_it; // 默认构造表示流结束
14
15 std::vector<int> numbers;
16
17 // 方法1:使用循环
18 while (input_it != end_it) {
19 numbers.push_back(*input_it);
20 ++input_it;
21 }
22
23 // 方法2:使用算法
24 std::istringstream iss2("10 20 30");
25 std::copy(std::istream_iterator<int>(iss2),
26 std::istream_iterator<int>(),
27 std::back_inserter(numbers));
28
29 // 方法3:直接构造
30 std::istringstream iss3("100 200");
31 std::vector<int> more_numbers(std::istream_iterator<int>(iss3),
32 std::istream_iterator<int>());
33
34 // 输出结果
35 for (int n : numbers) {
36 std::cout << n << " ";
37 }
38
39 return 0;
40}
向输出流写入数据:
xxxxxxxxxx
311
2
3
4
5
6int main() {
7 std::vector<int> numbers = {1, 2, 3, 4, 5};
8
9 // 创建输出流迭代器
10 std::ostream_iterator<int> output_it(std::cout, " ");
11
12 // 方法1:手动输出
13 for (int n : numbers) {
14 *output_it = n;
15 ++output_it;
16 }
17 std::cout << std::endl;
18
19 // 方法2:使用算法
20 std::copy(numbers.begin(), numbers.end(),
21 std::ostream_iterator<int>(std::cout, ", "));
22 std::cout << std::endl;
23
24 // 方法3:输出到字符串流
25 std::ostringstream oss;
26 std::copy(numbers.begin(), numbers.end(),
27 std::ostream_iterator<int>(oss, "|"));
28 std::cout << "字符串: " << oss.str() << std::endl;
29
30 return 0;
31}
流迭代器确实是迭代器,但也可以归类为迭代器适配器。原因:
适配不同的数据源 流迭代器将流(stream)这种数据源适配成了迭代器接口。
桥接两种不同的抽象
流(Stream):面向字符/字节的顺序读写抽象
迭代器(Iterator):面向元素的遍历抽象
流迭代器充当了这两种抽象之间的桥梁。
所有迭代器适配器都有类似的特点:
适配器类型 | 适配的对象 | 提供的新接口 |
---|---|---|
reverse_iterator | 普通迭代器 | 反向遍历接口 |
insert_iterator | 容器 | 插入操作的迭代器接口 |
move_iterator | 普通迭代器 | 移动语义的迭代器接口 |
istream_iterator | 输入流 | 输入迭代器接口 |
ostream_iterator | 输出流 | 输出迭代器接口 |
普通迭代器:
直接操作容器中的元素
是容器的原生接口
迭代器适配器:
包装其他对象(流、容器、迭代器)
提供统一的迭代器接口
使得原本不支持迭代器的对象能够与 STL 算法配合使用
流迭代器被称为"适配器"是因为它们的主要作用是适配——将流这种非迭代器接口的对象包装成迭代器接口,从而能够与 STL 的算法和容器无缝配合。这体现了适配器模式的核心思想:让不兼容的接口能够协同工作。
所以流迭代器既是迭代器(实现了迭代器接口),也是适配器(适配了流接口),这两个身份并不矛盾。
xxxxxxxxxx
311
2
3
4
5
6
7int main() {
8 // 输入数据
9 std::istringstream input("1 2 3 4 5 6 7 8 9 10");
10
11 // 读取数据
12 std::vector<int> numbers(std::istream_iterator<int>(input),
13 std::istream_iterator<int>());
14
15 // 处理数据:筛选偶数并乘以2
16 std::vector<int> processed;
17 std::copy_if(numbers.begin(), numbers.end(),
18 std::back_inserter(processed),
19 [](int n) { return n % 2 == 0; });
20
21 std::transform(processed.begin(), processed.end(),
22 processed.begin(),
23 [](int n) { return n * 2; });
24
25 // 输出结果
26 std::cout << "处理结果: ";
27 std::copy(processed.begin(), processed.end(),
28 std::ostream_iterator<int>(std::cout, " "));
29
30 return 0;
31}
xxxxxxxxxx
251
2
3
4
5
6
7int main() {
8 std::vector<int> vec = {1, 3, 5};
9 std::list<int> lst = {2, 4, 6};
10 std::vector<int> result;
11
12 // 合并两个容器
13 std::copy(vec.begin(), vec.end(), std::back_inserter(result));
14 std::copy(lst.begin(), lst.end(), std::back_inserter(result));
15
16 // 排序
17 std::sort(result.begin(), result.end());
18
19 // 反向输出
20 std::cout << "反向结果: ";
21 std::copy(result.rbegin(), result.rend(),
22 std::ostream_iterator<int>(std::cout, " "));
23
24 return 0;
25}
xxxxxxxxxx
301
2
3
4
5
6
7int main() {
8 std::vector<std::string> source = {
9 "very_long_string_1",
10 "very_long_string_2",
11 "very_long_string_3"
12 };
13
14 std::vector<std::string> destination;
15
16 // 高效移动(避免拷贝)
17 destination.reserve(source.size());
18 std::move(source.begin(), source.end(),
19 std::back_inserter(destination));
20
21 // 或者使用移动迭代器
22 std::vector<std::string> another_dest;
23 std::copy(std::make_move_iterator(source.begin()),
24 std::make_move_iterator(source.end()),
25 std::back_inserter(another_dest));
26
27 std::cout << "移动完成,source 可能为空\n";
28
29 return 0;
30}
插入迭代器:back_inserter
, front_inserter
, inserter
反向迭代器:reverse_iterator
, rbegin()
, rend()
移动迭代器:move_iterator
, make_move_iterator
流迭代器:istream_iterator
, ostream_iterator
算法适配器是(Algorithm Adapters)STL中用于修改或增强算法行为的工具,它们通过包装函数对象来改变算法的执行方式。
std::bind
是最重要的函数适配器,用于绑定函数参数:
xxxxxxxxxx
241
2
3
4
5
6bool greater_than(int x, int threshold) {
7 return x > threshold;
8}
9
10int main() {
11 std::vector<int> vec = {1, 5, 3, 8, 2, 9, 4};
12
13 // 使用 bind 创建一个"大于5"的谓词
14 auto greater_than_5 = std::bind(greater_than, std::placeholders::_1, 5);
15
16 // 查找第一个大于5的元素
17 auto it = std::find_if(vec.begin(), vec.end(), greater_than_5);
18
19 if (it != vec.end()) {
20 std::cout << "Found: " << *it << std::endl; // 输出: Found: 8
21 }
22
23 return 0;
24}
绑定成员函数
xxxxxxxxxx
431
2
3
4
5
6class Person {
7public:
8 std::string name;
9 int age;
10
11 Person(const std::string& n, int a) : name(n), age(a) {}
12
13 bool is_adult() const {
14 return age >= 18;
15 }
16
17 void print() const {
18 std::cout << name << " (" << age << ")" << std::endl;
19 }
20};
21
22int main() {
23 std::vector<Person> people = {
24 {"Alice", 25},
25 {"Bob", 16},
26 {"Charlie", 30},
27 {"David", 15}
28 };
29
30 // 绑定成员函数
31 auto is_adult = std::bind(&Person::is_adult, std::placeholders::_1);
32 auto print_person = std::bind(&Person::print, std::placeholders::_1);
33
34 // 找到所有成年人
35 std::cout << "Adults:" << std::endl;
36 std::for_each(people.begin(), people.end(), [&](const Person& p) {
37 if (is_adult(p)) {
38 print_person(p);
39 }
40 });
41
42 return 0;
43}
占位符(Placeholders)是 std::bind
的核心机制,用于指定在调用绑定函数时哪些参数位置需要由调用者提供。
占位符定义在 std::placeholders
命名空间中,用于标记参数的位置:
std::placeholders::_1
- 第一个参数
std::placeholders::_2
- 第二个参数
std::placeholders::_3
- 第三个参数
... 以此类推
xxxxxxxxxx
201
2
3
4int add(int a, int b, int c) {
5 return a + b + c;
6}
7
8int main() {
9 // 绑定第一个参数为 10,其他两个参数由调用时提供
10 auto bound1 = std::bind(add, 10, std::placeholders::_1,
11 std::placeholders::_2);
12 std::cout << bound1(5, 3) << std::endl; // 输出: 18 (10 + 5 + 3)
13
14 // 绑定第二个参数为 20,第一和第三个参数由调用时提供
15 auto bound2 = std::bind(add, std::placeholders::_1, 20,
16 std::placeholders::_2);
17 std::cout << bound2(1, 4) << std::endl; // 输出: 25 (1 + 20 + 4)
18
19 return 0;
20}
定义于头文件 <functional>
template< class F, class... Args > /unspecified/ bind( F&& f, Args&&... args ); (C++11 起) (C++20 前) template< class F, class... Args > constexpr /unspecified/ bind( F&& f, Args&&... args ); (C++20 起) template< class R, class F, class... Args > /unspecified/ bind( F&& f, Args&&... args ); (C++11 起) (C++20 前) template< class R, class F, class... Args > constexpr /unspecified/ bind( F&& f, Args&&... args ); (C++20 起) 函数模板
bind
生成f
的转发调用包装器。调用此包装器等价于以一些绑定到args
的参数调用f
。
上面是cppreference
中bind
的声明形式,第一个参数看起来是一个右值引用,为什么在真正使用过程中,传入一些左值也可以呢?
这涉及到模板中的引用折叠,引用折叠是 C++11 引入的一个重要概念,主要用于处理模板参数推导和完美转发中的引用类型组合问题。
当在模板中出现"引用的引用"时,C++ 编译器会根据特定规则将它们"折叠"成单一的引用类型。
引用折叠遵循以下四条规则:
xxxxxxxxxx
51// 引用折叠规则表
2T& & -> T& // 左值引用 + 左值引用 = 左值引用
3T& && -> T& // 左值引用 + 右值引用 = 左值引用
4T&& & -> T& // 右值引用 + 左值引用 = 左值引用
5T&& && -> T&& // 右值引用 + 右值引用 = 右值引用
只有当两个都是右值引用时,结果才是右值引用;否则都是左值引用。
bind的默认传递方式是值传递,如果在bind中想要使用引用传递,需要用到引用包装器。
引用包装器是C++11引入的一个重要工具类,主要用于解决引用无法直接存储在容器中的问题。
std::reference_wrapper<T>
是一个类模板,它包装了对类型T对象的引用,使得引用可以像对象一样被复制、赋值和存储。
通过std::ref
和std::cref
,我们可以在使用的时候直接创建引用包装器,从而在容器、算法和函数绑定中安全高效地使用引用,避免不必要的对象拷贝,提高程序性能。
在bind传参时可以使用std::ref,如果func函数中原本形参形式为const引用,相应地可以使用std::cref。
std::bind
的返回值是一个未指定类型的函数对象,这个类型是实现定义的,但它满足特定的接口要求。
std::bind
的返回值是:
实现定义的可调用类型: 具体类型由编译器决定
满足 Callable 概念: 可以使用 operator()
调用
可复制可移动: 支持标准的值语义
类型擦除友好: 可以存储在 std::function
中
支持完美转发: 保持参数的值类别
在现代C++中,通常使用 auto
接收 std::bind
的返回值,或者在需要类型擦除时使用 std::function
。对于简单的绑定场景,Lambda表达式通常是更好的选择。
1. 未指定的可调用类型
xxxxxxxxxx
181
2
3
4
5int add(int a, int b) {
6 return a + b;
7}
8
9int main() {
10 auto bound_func = std::bind(add, 10, std::placeholders::_1);
11
12 // 返回值类型是实现定义的,通常类似于:
13 // std::_Bind<int(*)(int, int), int, std::_Placeholder<1>>
14 std::cout << "Type: " << typeid(bound_func).name() << std::endl;
15
16 // 但我们通常使用 auto 或 std::function 来接收
17 return 0;
18}
2. 可调用对象 (Callable)
xxxxxxxxxx
191
2
3
4void print_sum(int a, int b, int c) {
5 std::cout << "Sum: " << (a + b + c) << std::endl;
6}
7
8int main() {
9 // bind 返回的对象是可调用的
10 auto bound_func = std::bind(print_sum, 1, std::placeholders::_1, 3);
11
12 // 可以像函数一样调用
13 bound_func(2); // 输出: Sum: 6
14
15 // 也可以使用 operator()
16 bound_func.operator()(2); // 同样输出: Sum: 6
17
18 return 0;
19}
1. 使用auto
xxxxxxxxxx
151
2
3
4int multiply(int a, int b) {
5 return a * b;
6}
7
8int main() {
9 // 最简洁的方式
10 auto bound_multiply = std::bind(multiply, std::placeholders::_1, 5);
11
12 std::cout << bound_multiply(4) << std::endl; // 输出: 20
13
14 return 0;
15}
2. 使用 std::function
xxxxxxxxxx
261
2
3
4
5int add(int a, int b) {
6 return a + b;
7}
8
9int subtract(int a, int b) {
10 return a - b;
11}
12
13int main() {
14 // 使用 std::function 可以存储不同的 bind 结果
15 std::vector<std::function<int(int)>> operations;
16
17 operations.push_back(std::bind(add, std::placeholders::_1, 10));
18 operations.push_back(std::bind(subtract, std::placeholders::_1, 5));
19
20 for (const auto& op : operations) {
21 std::cout << op(20) << " "; // 输出: 30 15
22 }
23 std::cout << std::endl;
24
25 return 0;
26}
3. 模板参数推导
xxxxxxxxxx
211
2
3
4template<typename Func>
5void execute_twice(Func f, int value) {
6 std::cout << f(value) << " ";
7 std::cout << f(value * 2) << std::endl;
8}
9
10int square(int x) {
11 return x * x;
12}
13
14int main() {
15 auto bound_square = std::bind(square, std::placeholders::_1);
16
17 // 模板可以自动推导 bind 返回的类型
18 execute_twice(bound_square, 3); // 输出: 9 36
19
20 return 0;
21}
1. 可复制和可移动
xxxxxxxxxx
211
2
3
4int add_three(int a, int b, int c) {
5 return a + b + c;
6}
7
8int main() {
9 auto original = std::bind(add_three, 1, 2, std::placeholders::_1);
10
11 // 可以复制
12 auto copy = original;
13
14 // 可以移动
15 auto moved = std::move(original);
16
17 std::cout << copy(3) << std::endl; // 输出: 6
18 std::cout << moved(4) << std::endl; // 输出: 7
19
20 return 0;
21}
2. 支持完美转发
xxxxxxxxxx
171
2
3
4
5void process_string(const std::string& s) {
6 std::cout << "Processing: " << s << std::endl;
7}
8
9int main() {
10 auto bound_process = std::bind(process_string, std::placeholders::_1);
11
12 std::string str = "Hello";
13 bound_process(str); // 传递左值
14 bound_process(std::string("World")); // 传递右值
15
16 return 0;
17}
3. 嵌套绑定
xxxxxxxxxx
231
2
3
4int add(int a, int b) {
5 return a + b;
6}
7
8int multiply(int a, int b) {
9 return a * b;
10}
11
12int main() {
13 // bind 的返回值可以作为另一个 bind 的参数
14 auto add_5 = std::bind(add, std::placeholders::_1, 5);
15 auto multiply_and_add = std::bind(add,
16 std::bind(multiply, std::placeholders::_1, 2),
17 std::bind(add_5, std::placeholders::_1));
18
19 // 相当于: (x * 2) + (x + 5)
20 std::cout << multiply_and_add(3) << std::endl; // 输出: 14 (3*2 + 3+5)
21
22 return 0;
23}
std::function
是C++11引入的一个通用函数包装器,它可以存储、复制和调用任何可调用对象(函数、Lambda表达式、函数对象、成员函数等)。作为函数适配器,它提供了统一的接口来处理不同类型的可调用实体。
xxxxxxxxxx
331
2
3
4// 普通函数
5int add(int a, int b) {
6 return a + b;
7}
8
9// 函数对象
10struct Multiply {
11 int operator()(int a, int b) const {
12 return a * b;
13 }
14};
15
16int main() {
17 // std::function 可以存储不同类型的可调用对象
18 std::function<int(int, int)> func;
19
20 // 存储普通函数
21 func = add;
22 std::cout << func(3, 4) << std::endl; // 输出: 7
23
24 // 存储函数对象
25 func = Multiply{};
26 std::cout << func(3, 4) << std::endl; // 输出: 12
27
28 // 存储Lambda表达式
29 func = [](int a, int b) { return a - b; };
30 std::cout << func(3, 4) << std::endl; // 输出: -1
31
32 return 0;
33}
xxxxxxxxxx
241
2
3
4int main() {
5 // 声明语法:std::function<返回类型(参数类型列表)>
6 std::function<void()> func1; // 无参数,无返回值
7 std::function<int(int)> func2; // 一个int参数,返回int
8 std::function<bool(const std::string&)> func3; // string引用参数,返回bool
9
10 // 检查是否为空
11 if (!func1) {
12 std::cout << "func1 is empty" << std::endl;
13 }
14
15 // 赋值
16 func1 = []() { std::cout << "Hello World!" << std::endl; };
17
18 // 调用
19 if (func1) {
20 func1(); // 输出: Hello World!
21 }
22
23 return 0;
24}
1. 统一不同类型的可调用对象
xxxxxxxxxx
561
2
3
4
5// 普通函数
6void print_message(const std::string& msg) {
7 std::cout << "Function: " << msg << std::endl;
8}
9
10// 函数对象
11class Printer {
12public:
13 void operator()(const std::string& msg) const {
14 std::cout << "Functor: " << msg << std::endl;
15 }
16};
17
18// 类成员函数
19class Logger {
20public:
21 void log(const std::string& msg) const {
22 std::cout << "Member: " << msg << std::endl;
23 }
24};
25
26int main() {
27 // 使用 std::function 统一存储不同类型的可调用对象
28 std::vector<std::function<void(const std::string&)>> handlers;
29
30 // 添加普通函数
31 handlers.push_back(print_message);
32
33 // 添加函数对象
34 handlers.push_back(Printer{});
35
36 // 添加Lambda表达式
37 handlers.push_back([](const std::string& msg) {
38 std::cout << "Lambda: " << msg << std::endl;
39 });
40
41 // 添加成员函数(需要绑定对象)
42 Logger logger;
43 handlers.push_back(std::bind(&Logger::log, &logger, std::placeholders::_1));
44
45 // 统一调用
46 for (const auto& handler : handlers) {
47 handler("Hello");
48 }
49 // 输出:
50 // Function: Hello
51 // Functor: Hello
52 // Lambda: Hello
53 // Member: Hello
54
55 return 0;
56}
2. 回调函数系统
xxxxxxxxxx
631
2
3
4
5
6class EventSystem {
7public:
8 using EventHandler = std::function<void(const std::string&)>;
9
10 // 注册事件处理器
11 void register_handler(const std::string& event, EventHandler handler) {
12 handlers_[event].push_back(handler);
13 }
14
15 // 触发事件
16 void trigger_event(const std::string& event, const std::string& data) {
17 if (handlers_.find(event) != handlers_.end()) {
18 for (const auto& handler : handlers_[event]) {
19 handler(data);
20 }
21 }
22 }
23
24private:
25 std::map<std::string, std::vector<EventHandler>> handlers_;
26};
27
28// 不同的事件处理方式
29void on_user_login(const std::string& username) {
30 std::cout << "User logged in: " << username << std::endl;
31}
32
33class SecurityMonitor {
34public:
35 void on_security_event(const std::string& event) {
36 std::cout << "Security alert: " << event << std::endl;
37 }
38};
39
40int main() {
41 EventSystem event_system;
42 SecurityMonitor monitor;
43
44 // 注册不同类型的处理器
45 event_system.register_handler("login", on_user_login);
46
47 event_system.register_handler("login",
48 std::bind(&SecurityMonitor::on_security_event, &monitor, std::placeholders::_1));
49
50 event_system.register_handler("login",
51 [](const std::string& user) {
52 std::cout << "Lambda handler: Welcome " << user << "!" << std::endl;
53 });
54
55 // 触发事件
56 event_system.trigger_event("login", "Alice");
57 // 输出:
58 // User logged in: Alice
59 // Security alert: Alice
60 // Lambda handler: Welcome Alice!
61
62 return 0;
63}
std::mem_fn
是C++11引入的一个函数适配器,专门用于将成员函数转换为可调用的函数对象。它提供了一种简洁的方式来处理成员函数指针,使其能够与STL算法和其他需要函数对象的场景配合使用。
xxxxxxxxxx
441
2
3
4class Person {
5public:
6 Person(const std::string& name, int age) : name_(name), age_(age) {}
7
8 void print() const {
9 std::cout << name_ << " (" << age_ << ")" << std::endl;
10 }
11
12 int get_age() const {
13 return age_;
14 }
15
16 void set_age(int age) {
17 age_ = age;
18 }
19
20 const std::string& get_name() const {
21 return name_;
22 }
23
24private:
25 std::string name_;
26 int age_;
27};
28
29int main() {
30 Person person("Alice", 25);
31
32 // 使用 std::mem_fn 包装成员函数
33 auto print_func = std::mem_fn(&Person::print);
34 auto get_age_func = std::mem_fn(&Person::get_age);
35 auto set_age_func = std::mem_fn(&Person::set_age);
36
37 // 调用包装后的函数
38 print_func(person); // 输出: Alice (25)
39 std::cout << get_age_func(person) << std::endl; // 输出: 25
40 set_age_func(person, 30);
41 print_func(person); // 输出: Alice (30)
42
43 return 0;
44}