C++ 笔记:std::bind 函数模板详解

std::bind 是 C++11 引入的函数适配器 ,定义在 <functional> 头文件中,核心作用是将函数与参数绑定,生成一个可调用对象。它可以延迟函数调用、固定部分参数、修改函数调用形式,是 C++ 中实现函数参数绑定、回调函数封装的核心工具。

本文从基础用法、参数绑定、占位符、绑定成员函数、实战场景等维度,系统讲解 std::bind 的使用方法与原理。

一、std::bind 基础概念

1. 占位符基本用法

#include <iostream> #include <functional> void show(int a, int b, int c) { std::cout << a << " " << b << " " << c << std::endl; } int main() { using namespace std::placeholders; // 简化占位符使用 // _1:第一个动态参数,_2:第二个动态参数 auto func = std::bind(show, _1, 100, _2); func(10, 20); // 输出:10 100 20 // 调整参数顺序:交换 _1 和 _2 auto func_swap = std::bind(show, _2, _1, 30); func_swap(10, 20); // 输出:20 10 30 return 0; }

2. 占位符与固定参数混合

  1. 本质std::bind 是一个模板函数,接收一个可调用对象(普通函数、函数指针、lambda、成员函数、仿函数)和若干参数,返回一个新的可调用对象。

  2. 核心能力

    • 固定函数的部分参数(参数绑定);
    • 调整参数的顺序;
    • 将成员函数转换为普通可调用对象;
    • 延迟函数执行。
  3. 头文件 :使用前必须包含

    cpp 复制代码
    #include <functional>

    二、基础用法:绑定普通函数

    1. 无参数绑定(直接封装函数)

    直接用 bind 封装无参函数,生成可调用对象,调用时执行原函数。

    cpp 复制代码
    #include <iostream>
    #include <functional> // 必须包含
    
    // 普通无参函数
    void print() {
        std::cout << "Hello std::bind!" << std::endl;
    }
    
    int main() {
        // 绑定函数,生成可调用对象 func
        auto func = std::bind(print);
        func(); // 调用:输出 Hello std::bind!
        return 0;
    }

    2. 固定参数(绑定实参)

    绑定函数时直接传入参数值,调用时无需重复传参,参数被永久固定。

    cpp 复制代码
    #include <iostream>
    #include <functional>
    
    // 带参数的普通函数
    int add(int a, int b) {
        return a + b;
    }
    
    int main() {
        // 绑定 add 函数,固定参数 10 和 20
        auto add_10_20 = std::bind(add, 10, 20);
        std::cout << add_10_20() << std::endl; // 输出:30
    
        // 固定第一个参数为 5,第二个参数后续传入
        auto add_5 = std::bind(add, 5, std::placeholders::_1);
        std::cout << add_5(15) << std::endl; // 输出:20(5+15)
        return 0;
    }

    三、占位符:std::placeholders

    std::placeholdersstd::bind参数占位符 ,用 _1、_2、_3... 表示调用时才传入的参数,用于:

  4. 保留参数位置;

  5. 调整参数顺序;

  6. 动态传参。

cpp 复制代码
#include <iostream>
#include <functional>

void show(int a, int b, int c) {
    std::cout << a << " " << b << " " << c << std::endl;
}

int main() {
    using namespace std::placeholders; // 简化占位符使用

    // _1:第一个动态参数,_2:第二个动态参数
    auto func = std::bind(show, _1, 100, _2);
    func(10, 20); // 输出:10 100 20

    // 调整参数顺序:交换 _1 和 _2
    auto func_swap = std::bind(show, _2, _1, 30);
    func_swap(10, 20); // 输出:20 10 30
    return 0;
}
  • _1 对应调用时的第一个实参_2 对应第二个实参,以此类推;
  • 占位符的数量没有上限,根据函数参数个数灵活使用。

2. 占位符与固定参数混合

绑定函数时,可混合使用固定值和占位符,灵活控制参数:

cpp 复制代码
// 函数:a - b
int sub(int a, int b) { return a - b; }

int main() {
    using namespace std::placeholders;
    // 固定 b=5,a 动态传入
    auto sub_5 = std::bind(sub, _1, 5);
    std::cout << sub_5(20) << std::endl; // 20-5=15

    // 固定 a=30,b 动态传入
    auto sub_30 = std::bind(sub, 30, _1);
    std::cout << sub_30(10) << std::endl; // 30-10=20
    return 0;
}

四、进阶用法:绑定类成员函数

std::bind 最常用的场景之一是绑定类的成员函数 。注意:类成员函数隐含 this 指针 ,绑定时必须传入对象(或对象指针 / 引用) 作为第一个参数。

1. 绑定普通成员函数

cpp 复制代码
#include <iostream>
#include <functional>
#include <string>

class Person {
private:
    std::string name;
public:
    Person(std::string n) : name(n) {}

    // 成员函数
    void showInfo(int age) {
        std::cout << "姓名:" << name << ",年龄:" << age << std::endl;
    }
};

int main() {
    using namespace std::placeholders;
    Person p("张三"); // 创建对象

    // 绑定成员函数:第一个参数是 &成员函数,第二个参数是对象地址/对象
    auto func = std::bind(&Person::showInfo, &p, _1);
    func(20); // 输出:姓名:张三,年龄:20

    // 直接传对象(会拷贝对象)
    auto func2 = std::bind(&Person::showInfo, p, 25);
    func2(); // 输出:姓名:张三,年龄:25
    return 0;
}

2. 绑定静态成员函数

静态成员函数this 指针,绑定时无需传入对象,直接使用类名调用:

cpp 复制代码
class Test {
public:
    static void staticFunc(int a) {
        std::cout << "静态函数:" << a << std::endl;
    }
};

int main() {
    auto func = std::bind(&Test::staticFunc, std::placeholders::_1);
    func(100); // 输出:静态函数:100
    return 0;
}

五、绑定仿函数与 lambda 表达式

std::bind 支持绑定所有可调用对象,包括仿函数(函数对象)lambda 表达式

cpp 复制代码
#include <iostream>
#include <functional>

// 仿函数
struct Multiply {
    int operator()(int a, int b) {
        return a * b;
    }
};

int main() {
    // 1. 绑定仿函数
    Multiply mul;
    auto func1 = std::bind(mul, 6, 7);
    std::cout << func1() << std::endl; // 42

    // 2. 绑定 lambda 表达式
    auto lambda = [](int a, int b) { return a + b; };
    auto func2 = std::bind(lambda, std::placeholders::_1, 8);
    std::cout << func2(12) << std::endl; // 20
    return 0;
}

六、std::bind 与 std::function 配合使用

std::function 是可调用对象的包装器,std::bind 生成的可调用对象可以存入 std::function,实现统一的函数接口管理(常用于回调函数、事件处理)

cpp 复制代码
#include <iostream>
#include <functional>

// 回调函数类型:int -> int
using Callback = std::function<int(int)>;

int square(int x) { return x * x; }

// 接收回调函数
void process(int num, Callback cb) {
    std::cout << "结果:" << cb(num) << std::endl;
}

int main() {
    // 用 bind 封装函数,传入 std::function
    auto func = std::bind(square, std::placeholders::_1);
    process(5, func); // 输出:结果:25
    return 0;
}

七、注意事项

八、总结

std::bind 是 C++ 中强大的函数适配器,核心价值是灵活绑定函数与参数,简化可调用对象的封装与调用。

掌握 std::bind,能大幅提升 C++ 中函数封装、回调机制的代码灵活性与可读性。

总结

  1. 参数拷贝std::bind 绑定参数时,默认会拷贝参数值 。如果需要传递引用,必须使用 std::ref(引用)或 std::cref(常量引用):

    cpp 复制代码
    void setValue(int& a) { a = 100; }
    int main() {
        int x = 0;
        auto func = std::bind(setValue, std::ref(x)); // 必须用 ref 传递引用
        func();
        std::cout << x << std::endl; // 输出:100
        return 0;
    }
  2. 占位符作用域 :占位符 _1、_2... 位于 std::placeholders 命名空间下,建议使用 using namespace std::placeholders; 简化代码。

  3. 成员函数绑定 :必须取地址 &类名::成员函数,且第一个参数必须是对象 / 对象指针。

  4. 替代方案 :C++11 之后,lambda 表达式 可以替代大部分 std::bind 简单场景,但 bind 在绑定成员函数、灵活调整参数顺序时更简洁。

  5. 基础:绑定普通函数,固定参数、动态传参;

  6. 核心:std::placeholders 占位符,控制参数位置与顺序;

  7. std::bind 是 C++11 函数适配器,用于生成可调用对象,需包含<functional>头文件;

  8. 核心用法:绑定普通函数 / 成员函数 / 仿函数,用_1、_2占位符实现动态传参、调整参数顺序;

  9. 绑定成员函数必须传入对象 / 指针,传引用需用std::ref

  10. 常与std::function配合,用于回调函数、参数固定、延迟调用等场景。

    • 重点:绑定类成员函数,必须传入对象 / 指针;
    • 实战:配合 std::function 实现回调函数、统一接口管理。
相关推荐
happymaker06262 小时前
请求头 & 文件下载 & JSP 内置对象实战
java·前端·servlet
skywalk81632 小时前
Kotti Next的tinyfrontend前端生成和测试(使用WorkBuddy)
前端
m0_647057962 小时前
【无标题】
前端·人工智能
Oll Correct2 小时前
实验十三:IPv4子网划分与基础路由配置实验——基于Cisco Packet Tracer的跨网段通信验证
网络·笔记
北城笑笑2 小时前
Frontend 与 FPGA 深度融合实战解析:从技术协同到多场景落地( 前端和现场可编程门阵列 )
前端·websocket·3d·vue·fpga
ljt27249606612 小时前
Compose笔记(七十五)--withFrameNanos
笔记·android jetpack
CoderCodingNo2 小时前
【GESP】C++五级练习题 luogu-P1303 A*B Problem | 高精度计算
数据结构·c++·算法
故事和你912 小时前
洛谷-算法1-1-模拟与高精度2
开发语言·数据结构·c++·算法·动态规划
B1acktion2 小时前
2.6.堆排序——从堆结构到 Top-K,一套思路贯穿排序与选择
数据结构·c++·算法·排序算法