std::bind
是 C++11 引入的一个函数适配器,它可以将函数或可调用对象与其参数绑定在一起,在调用时,减少传入的参数数量,从而简化函数调用。
1. 使用
#if 1 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<functional> using namespace std; void example(int a, int b, int c) { cout << "a=" << a << ", b=" << b << ", c=" << c << endl; } void test01() { // 绑定时:将 bind 的参数按照顺序绑定到被绑定函数上 // 调用时:第一个位置参数对应 placeholders::_1,第二个位置的参数对应 placeholders::_2 ... 以此类推 // 占位符允许你重新排列参数的顺序 auto func1 = bind(example, placeholders::_2, 100, placeholders::_1); func1(200, 300); auto func2 = bind(example, 100, placeholders::_1, placeholders::_1); func2(200, 300); // 占位符必须从 placeholders::_1 开始 // auto func3 = bind(example, 100, placeholders::_2, placeholders::_3); // func3(200, 300); } struct Functor { void operator()(int a, int b) { cout << "a=" << a << ", b=" << b << endl; } }; struct Demo { void sample(int a, int b) { cout << "a=" << a << ", b=" << b << endl; } static void instance(int a, int b) { cout << "a=" << a << ", b=" << b << endl; } }; void test02() { // 1. 适配函数对象 auto func1 = bind(Functor(), 100, placeholders::_1); func1(200); // 2. 适配成员函数 // 2.1 普通成员函数,必须提供对象实例 Demo demo; auto func2 = bind(&Demo::sample, demo, 100, placeholders::_1); func2(200); // 2.2 静态成员函数,不需要提供对象实例 auto func3 = bind(&Demo::instance, 100, placeholders::_1); func3(200); // 3. 适配匿名函数 auto lambda = [](int a, int b) { cout << "a=" << a << ", b=" << b << endl; }; auto func4 = bind(lambda, 100, placeholders::_1); func4(200); } int main() { test02(); return EXIT_SUCCESS; } #endif
2. 探讨
这一小节主要探讨l两个话题。
- bind 函数返回值
- bind 绑定参数
示例代码:
#if 1 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<functional> using namespace std; void example(int a, int b) { cout << "a=" << a << ", b=" << b << endl; } // 1. bind 返回类型 void test01() { // _Binder 类型 auto func1 = bind(example, placeholders::_1, 100); cout << typeid(func1).name() << endl; func1(200); // 注意:func1(200, 300, 400) 也是可以,只是忽略后面的参数 // function 类型 function<void(int)> func2 = bind(example, placeholders::_1, 100); cout << typeid(func2).name() << endl; func2(200); // _Binder 保持较轻量的对象封装,直接存储函数和参数的引用或副本,执行时开销较低 // function 提供对可调用对象 _Binder 的又一层封装,提供了更为间接的调用,需要一定的开销 } // 2. bind 参数拷贝 struct Person { Person() { m_num = 100; cout << "默认构造" << endl; } Person(const Person&) { cout << "拷贝构造" << endl; } Person(Person&&) noexcept { cout << "移动构造" << endl; } int m_num; }; // 此处修改为引用,如果不希望修改外部对象可以使用 const 引用 void demo(int a, Person& b) { cout << "a=" << a << " b=" << &b << endl; b.m_num = 200; } void test02() { Person person; // 参数进行绑定时,默认需要拷贝到 bind 函数中 // 使用 ref 引用包装器避免参数传递到 bind 函数时进行拷贝 auto func = bind(demo, placeholders::_1, ref(person)); func(100); cout << person.m_num << endl; } int main() { test01(); return EXIT_SUCCESS; } #endif
3. 思考
C++11 提供了 std::bind
来实现对函数进行参数绑定,以简化函数调用或预设部分参数,但它并不是最理想的解决方案。原因:
- 语法复杂,代码可读性差。
std::bind
需要使用占位符_1
,_2
等来表示参数的位置,这种写法对于简单的函数调用已经显得过于复杂,尤其在参数较多时,可读性下降。 - 无法捕获局部变量。
std::bind
只能绑定传递的参数,而无法捕获作用域内的局部变量。如果想要在绑定时使用上下文中的局部变量,必须额外通过引用或全局变量传递。 - 性能优势较低。
std::bind
实际上会创建一个包含函数指针和参数的复杂函数对象,这些参数存储和解析过程会引入一定的间接调用和运行时解析。
我们可以在大多数的场景下使用 lambda 匿名函数来代替 bind,相对于 bind 函数适配器,lambda 具有更多的优势。
示例代码:
#if 1 #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; void example(int a, int b) { cout << "a=" << a << ", b=" << b << endl; } struct Functor { void operator()(int a, int b, double c) { cout << "a=" << a << ", b=" << b << ", c=" << c << endl; } }; struct Demo { void sample(int a, int b) { cout << "a=" << a << ", b=" << b << endl; } static void instance(int a, int b) { cout << "a=" << a << ", b=" << b << endl; } }; void test() { // 1. 适配普通函数 auto func1 = [](int num) { return example(num, 100); }; func1(200); // 2. 适配函数对象 auto func2 = [](int num1, int num2) { return Functor()(num1, 100, num2); };; func2(200, 3.14); // 2. 适配成员函数 // 2.1 适配普通成员函数,必须提供对象实例 Demo person; auto func3 = [person](int num) mutable { return person.sample(num, 100); }; func3(200); // 2.2 适配静态成员函数,不需要提供对象实例 auto func4 = [](int num) { return Demo::instance(num, 100); }; func4(200); } int main() { test(); return EXIT_SUCCESS; } #endif