std::bind
是 C++11 引入的一个函数适配器,它可以将函数或可调用对象与其参数绑定在一起,在调用时,减少传入的参数数量,从而简化函数调用。
- 使用 {#title-0} ================
#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
- 探讨 {#title-1} ================
这一小节主要探讨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
- 思考 {#title-2} ================
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