掌握C++ std::bind:参数绑定与灵活调用

引言

在前面 STL 系列中,我们学习了仿函数和 Lambda。今天要讲的 std::bind 是另一种处理可调用对象的方式。它的核心作用很简单:把一个函数(或仿函数、Lambda)的部分参数提前绑定好,生成一个新的可调用对象

比如你有一个 func(int a, int b),你可以用 binda 绑定为 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::refstd::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 替代,只在重排参数顺序等特殊场景使用。

相关推荐
拽着尾巴的鱼儿1 小时前
Java 对象的深拷贝和浅拷贝
java·开发语言
数据法师1 小时前
Crow Translate :开源桌面划词翻译工具
c++·qt·开源
fie88891 小时前
matlab打靶法求解两点边值优化问题
开发语言·算法·matlab
skywalk81631 小时前
请结合以下说明,先完成类似python的内置函数。 然后再去完成内置库(标准款) ‌内置函数‌
开发语言·python
王璐WL2 小时前
【C++】经典易错题(2)
c++
我不是懒洋洋2 小时前
手写一个异步日志库:从printf到高性能无锁日志
java·c语言·开发语言·c++·visual studio
郝学胜-神的一滴2 小时前
Python 高级编程 018:深挖 super
开发语言·python·程序人生·软件构建
hetao17338372 小时前
2026-05-28~06-02 hetao1733837 的刷题记录
c++·算法
hoiii1872 小时前
基于MATLAB实现Lamb波频散曲线求解
开发语言·matlab