引言
在前面 STL 系列中,我们学习了仿函数和 Lambda。今天要讲的 std::bind 是另一种处理可调用对象的方式。它的核心作用很简单:把一个函数(或仿函数、Lambda)的部分参数提前绑定好,生成一个新的可调用对象。
比如你有一个 func(int a, int b),你可以用 bind 把 a 绑定为 10,生成一个新的函数 newFunc(int b),调用 newFunc(5) 就等价于 func(10, 5)。
在现代 C++ 中,Lambda 已经能覆盖 bind 的大部分用途,但 bind 在某些场景下仍然有用:配合占位符灵活重排参数顺序、与老式 STL 算法搭配等。

第一部分:基本语法
一、头文件与占位符
cpp
#include <iostream>
#include <functional> // bind 所在头文件
using namespace std;
using namespace std::placeholders; // _1, _2, _3 等占位符
占位符 :_1 表示新函数的第 1 个参数,_2 表示第 2 个参数,以此类推。std::bind 最多支持 29 个占位符(_1 到 _29)。
二、最简单的例子
cpp
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
// 原始函数:三个参数
void show(int a, int b, int c) {
cout << a << ", " << b << ", " << c << endl;
}
int main() {
// bind 所有参数
auto f1 = bind(show, 10, 20, 30);
f1(); // 10, 20, 30
// 只绑定部分参数,其余用占位符
auto f2 = bind(show, 100, _1, _2);
f2(200, 300); // 100, 200, 300
// 调换参数顺序
auto f3 = bind(show, _3, _2, _1);
f3(1, 2, 3); // 3, 2, 1(顺序被反转了!)
return 0;
}
第二部分:常见用法
一、绑定普通函数
cpp
int add(int a, int b) {
return a + b;
}
int main() {
// 绑定第一个参数为 10
auto add10 = bind(add, 10, _1);
cout << add10(5) << endl; // 15
cout << add10(20) << endl; // 30
// 绑定两个参数
auto add3and5 = bind(add, 3, 5);
cout << add3and5() << endl; // 8
return 0;
}
二、绑定成员函数
绑定成员函数时,第一个绑定的参数必须是对象的地址(或对象本身)。
cpp
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
class Calculator {
public:
int add(int a, int b) {
return a + b;
}
};
int main() {
Calculator calc;
// 绑定成员函数:第一个参数传对象地址
auto f = bind(&Calculator::add, &calc, _1, _2);
cout << f(3, 5) << endl; // 8
// 也可以传对象本身(会拷贝一份)
auto g = bind(&Calculator::add, calc, _1, _2);
cout << g(10, 20) << endl; // 30
return 0;
}
注意 :&Calculator::add 前面的 & 不能省略。bind 的第一个参数是成员函数指针,需要显式取地址。
三、绑定仿函数
cpp
struct Multiplier {
int operator()(int a, int b) const {
return a * b;
}
};
int main() {
Multiplier mul;
auto times5 = bind(mul, _1, 5);
cout << times5(3) << endl; // 15
return 0;
}
四、绑定 Lambda
cpp
int main() {
auto lambda = [](int a, int b, int c) {
return a + b + c;
};
// 绑定第一个参数为 100
auto f = bind(lambda, 100, _1, _2);
cout << f(20, 3) << endl; // 123
return 0;
}
第三部分:占位符的灵活使用
一、重排参数顺序
cpp
void print(int x, int y, int z) {
cout << x << " " << y << " " << z << endl;
}
int main() {
// 反转参数顺序
auto f = bind(print, _3, _2, _1);
f(1, 2, 3); // 3 2 1
// 重复使用某个占位符(同一个值传两次)
auto g = bind(print, _1, _1, _2);
g(10, 20); // 10 10 20
return 0;
}
二、占位符传递引用
当原始函数接受引用参数时,需要用 std::ref 或 std::cref 传递引用,否则 bind 默认是值传递(会拷贝一份)。
cpp
#include <functional>
void increment(int& x) {
x++;
}
int main() {
int n = 10;
// ❌ 错误:bind 默认值传递,n 被拷贝,原变量不会被修改
// auto f = bind(increment, n);
// ✅ 正确:用 ref 传递引用
auto f = bind(increment, ref(n));
f();
cout << n << endl; // 11
// cref 传递 const 引用
auto g = bind([](const int& x) { cout << x; }, cref(n));
g(); // 11
return 0;
}
第四部分:配合 STL 算法
一、配合 find_if
cpp
#include <algorithm>
#include <vector>
using namespace std;
bool isGreaterThan(int value, int threshold) {
return value > threshold;
}
int main() {
vector<int> v = {1, 5, 3, 8, 2};
// 用 bind 把 threshold 绑定为 4
auto it = find_if(v.begin(), v.end(), bind(isGreaterThan, _1, 4));
if (it != v.end()) cout << *it << endl; // 5(第一个大于4的)
return 0;
}
二、配合 transform
cpp
#include <algorithm>
#include <vector>
using namespace std;
int multiply(int a, int b) {
return a * b;
}
int main() {
vector<int> v = {1, 2, 3, 4, 5};
vector<int> result(v.size());
// 每个元素乘以 10
transform(v.begin(), v.end(), result.begin(), bind(multiply, _1, 10));
// result: 10 20 30 40 50
return 0;
}
第五部分:bind vs Lambda
| 对比项 | std::bind |
Lambda |
|---|---|---|
| 可读性 | 较差(占位符不直观) | 好(参数名明确) |
| 性能 | 可能引入额外开销 | 通常更优(可内联) |
| 灵活性 | 方便重排参数顺序 | 需要手动写逻辑 |
| 学习成本 | 需要理解占位符 | 语法直观 |
| 现代推荐 | 仅特殊场景 | 首选 |
cpp
// 同样的功能,两种写法
// bind 写法
auto f1 = bind(multiply, _1, 10);
transform(v.begin(), v.end(), result.begin(), f1);
// Lambda 写法(更直观)
transform(v.begin(), v.end(), result.begin(), [](int x) { return x * 10; });
总结
一、核心要点
| 要点 | 内容 |
|---|---|
| 头文件 | <functional> |
| 占位符 | _1, _2, _3...,命名空间 std::placeholders |
| 绑定普通函数 | bind(func, 实参或占位符...) |
| 绑定成员函数 | bind(&Class::method, &obj, 实参或占位符...) |
| 传递引用 | ref(obj) 或 cref(obj) |
二、一句话记忆
std::bind 把函数的部分参数提前绑定(固定值或占位符),生成参数更少的新可调用对象。成员函数需传对象地址,引用参数需用 ref。现代 C++ 中大部分场景用 Lambda 替代,只在重排参数顺序等特殊场景使用。