C++ 中,通用的函数适配器 std::bind 是一个强大而灵活的工具。它允许将可调用对象及其参数进行绑定,并生成一个新的可调用对象。在涉及回调机制、延迟函数执行、函数参数部分绑定等场景中,std::bind 的作用尤为突出。


一、什么是 std::bind

std::bind 是 C++ 标准库中的一个模板函数,用来将可调用对象(如普通函数、成员函数、Lambda 函数、仿函数等)与参数绑定在一起,生成一个新的函数对象或 std::function 对象。

其核心函数原型如下:

1
2
template< class Fx, class... Args >
function<> bind(Fx&& fx, Args&&... args);
  • Fx:可调用对象的类型,可以是普通函数、成员函数、Lambda 表达式、仿函数,甚至是另一个 std::function 对象。
  • Args:可变参数模板,表示要绑定的参数,可以是值、引用或占位符 std::placeholders::_n

返回值std::bind 返回一个新的可调用对象,通常为 std::function 类型的实例,可以像调用普通函数一样使用。


二、std::bind 的基本用法

2.1 普通函数的绑定

普通函数是 std::bind 支持的最基本的可调用对象。通过 std::bind,我们可以将部分参数绑定,从而生成一个新的函数对象。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <functional>

void show(int bh, const std::string& message) {
    std::cout << "编号 " << bh << ":" << message << std::endl;
}

int main() {
    using namespace std::placeholders;

    // 绑定普通函数,生成一个新的可调用对象
    std::function<void(int, const std::string&)> fn1 = std::bind(show, _1, _2);
    fn1(1, "我是一只傻傻鸟。");  // 调用绑定后的函数对象

    // 调整参数顺序的绑定
    std::function<void(const std::string&, int)> fn2 = std::bind(show, _2, _1);
    fn2("我是一只傻傻鸟。", 2);  // 交换参数位置后调用
}

在这个例子中:

  • _1_2 是占位符,表示我们可以在调用 fn1fn2 时指定这些参数。
  • std::bind 返回了一个新的函数对象 fn1fn2,它们可以接受相应的参数并调用原函数 show

三、支持多种可调用对象

std::bind 可以绑定多种类型的可调用对象,包括普通函数、成员函数、Lambda 表达式、仿函数等。

3.1 绑定静态成员函数

静态成员函数的本质与普通函数类似,因此可以直接通过 std::bind 进行绑定。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
struct AA {
    static void show(int bh, const std::string& message) {
        std::cout << "编号 " << bh << ":" << message << std::endl;
    }
};

int main() {
    using namespace std::placeholders;

    std::function<void(int, const std::string&)> fn3 = std::bind(AA::show, _1, _2);
    fn3(2, "我是一只傻傻鸟。");
}

3.2 绑定仿函数

仿函数(即重载了 operator() 的类)也可以通过 std::bind 进行绑定。仿函数常用于自定义函数对象或策略模式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
struct BB {
    void operator()(int bh, const std::string& message) {
        std::cout << "编号 " << bh << ":" << message << std::endl;
    }
};

int main() {
    using namespace std::placeholders;

    BB bb;
    std::function<void(int, const std::string&)> fn4 = std::bind(bb, _1, _2);
    fn4(3, "我是一只傻傻鸟。");
}

3.3 绑定 Lambda 表达式

Lambda 表达式是 C++11 引入的一种轻量级可调用对象,也可以通过 std::bind 进行部分参数绑定。

1
2
3
4
5
6
7
8
int main() {
    auto lambda = [](int bh, const std::string& message) {
        std::cout << "编号 " << bh << ":" << message << std::endl;
    };

    std::function<void(int, const std::string&)> fn5 = std::bind(lambda, std::placeholders::_1, std::placeholders::_2);
    fn5(4, "我是一只傻傻鸟。");
}

3.4 绑定类的非静态成员函数

绑定非静态成员函数时,必须传递类实例的指针或引用,确保调用时有具体的对象来操作。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
struct CC {
    void show(int bh, const std::string& message) {
        std::cout << "编号 " << bh << ":" << message << std::endl;
    }
};

int main() {
    CC cc;
    std::function<void(int, const std::string&)> fn6 = std::bind(&CC::show, &cc, std::placeholders::_1, std::placeholders::_2);
    fn6(5, "我是一只傻傻鸟。");
}

在绑定非静态成员函数时,&CC::show 表示成员函数指针,而 &cc 表示该成员函数操作的实例。

3.5 绑定可转换为函数指针的类对象

通过类型转换操作符,类的实例也可以被转换为函数指针,从而通过 std::bind 进行绑定。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
struct DD {
    using Fun = void (*)(int, const std::string&);
    operator Fun() const {
        return show;
    }
};

int main() {
    DD dd;
    std::function<void(int, const std::string&)> fn7 = std::bind(dd, std::placeholders::_1, std::placeholders::_2);
    fn7(6, "我是一只傻傻鸟。");
}

四、std::bind 的参数占位符与参数绑定

在使用 std::bind 进行绑定时,我们可以选择哪些参数在绑定时固定,哪些参数在调用时动态传入。这里需要使用 std::placeholders::_n 占位符。

  • _1, _2, _3 等占位符用于表示调用时的参数。
  • 非占位符参数将在绑定时固定。
1
2
3
4
5
6
7
int main() {
    using namespace std::placeholders;

    // 将第一个参数固定为 3,只需传递剩余的参数
    std::function<void(const std::string&)> fn8 = std::bind(show, 3, _1);
    fn8("我是一只傻傻鸟。");
}

五、总结

std::bind 是 C++ 中极其强大的函数适配器,它通过绑定可调用对象及其参数生成新的函数对象,从而提供了灵活的参数绑定和调用方式。无论是处理普通函数、成员函数,还是仿函数和 Lambda 表达式,std::bind 都能够简化代码并提高可读性。理解 std::bind 的用法,能够帮助开发者编写更为简洁和通用的代码,尤其是在需要延迟执行、回调函数或函数适配器的场景中。