std::bind 函数适配器

std::bind 是 C++11 引入的一个函数适配器,它可以将函数或可调用对象与其参数绑定在一起,返回一个新的可调用对象。将可调用对象部分绑定参数,减少可调用对象传入的参数数量,从而简化函数调用,或者适配某些特殊场景的函数调用。

1. 使用

#include <functional>

// bind(可调用对象, 参数1, 参数2, ...)
auto new_function = std::bind(callable, arg1, arg2, ...);

示例代码:

#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

未经允许不得转载:一亩三分地 » std::bind 函数适配器
评论 (0)

6 + 8 =