《C++11:bind绑定器与function包装器》

C++STL中提供了bind1绑定器,通常与函数对象一起使用。

函数对象是重载了operator()函数的对象。

将二元函数对象operator()的第一个参数绑定为固定的x来构造一元函数对象。返回绑定了第一个参数的函数对象。

将二元函数对象operator()的第二个参数绑定为固定的x来构造一元函数对象。返回绑定了第二个参数的函数对象。 

我们看以下代码:

#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
#include<ctime>

using namespace std;

template<typename Container>

void showContainer(Container &con)
{
	typename Container::iterator it = con.begin();
	for (; it != con.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

int main()
{
	vector<int> vec;
    srand(time(nullptr));
	for (int i = 0; i < 10; i++)
	{
		vec.push_back(rand() % 100 + 1);
	}

	showContainer(vec);
	sort(vec.begin(), vec.end()); // 默认从小到大排序
	showContainer(vec);

	// greater 二元函数对象
	sort(vec.begin(), vec.end(), greater<int>()); // 从大到小排序,传入函数对象
	showContainer(vec);

	return 0;
}

如果现在要将60按顺序插入到vector,那么就要在vector中找第一个小于60的数字。可以使用泛型算法中的find_if在指定范围中查找满足特定条件的元素。需要接收的是一个一元函数对象。

template <class InputIterator, class UnaryPredicate>
   InputIterator find_if (InputIterator first, InputIterator last, UnaryPredicate pred);

pred:一个一元谓词,是一个可调用对象,接收一个参数,返回布尔值,返回true表示条件满足。

此时,需要从vector中取一个对象跟60比,那么不能使用库中的greater或者less函数对象,因为它们是一个二元函数对象,接收两个值。

那我们应该怎样处理呢?就引入了绑定器bind1st、bind2nd。

bind1st和bind2nd

通过绑定器绑定一个二元函数对象,就可以得到一个一元函数对象。

bind1st:要在vector中找小于60的元素,可以将greater的第一个参数绑定为60,就将greater间接的变为了一元函数对象了,传参的时候只需要传给greater的第二个参数即可。

bind2nd:要在vector中小于60的元素,可以将less的第二个参数绑定为60,就将less间接的变为了一元函数对象了,传参的时候只需要传给less的第一个参数即可。

#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
#include<ctime>

using namespace std;

template<typename Container>

void showContainer(Container &con)
{
	typename Container::iterator it = con.begin();
	for (; it != con.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

int main()
{
	vector<int> vec;
    srand(time(nullptr));
	for (int i = 0; i < 10; i++)
	{
		vec.push_back(rand() % 100 + 1);
	}

	showContainer(vec);
	sort(vec.begin(), vec.end()); // 默认从小到大排序
	showContainer(vec);

	// greater 二元函数对象
	sort(vec.begin(), vec.end(), greater<int>()); // 从大到小排序,传入函数对象
	showContainer(vec);

	// 将60按顺序插入到vector中 在vector中找第一个小于60的元素
	auto it1 = find_if(vec.begin(), vec.end(), bind1st(greater<int>(), 60));
	if (it1 != vec.end())
	{
		vec.insert(it1, 60);
	}
	showContainer(vec);
	auto it2 = find_if(vec.begin(), vec.end(), bind2nd(less<int>(), 60));
	if (it2 != vec.end())
	{
		vec.insert(it2, 60);
	}
	showContainer(vec);

	return 0;
}

bind1st和bind2nd的底层实现原理

下面我们来模拟实现bind1st和bind2nd来探究以下它们的底层实现原理。

using namespace std;

template<typename Container>

void showContainer(Container &con)
{
	typename Container::iterator it = con.begin();
	for (; it != con.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

// 模拟实现find_if
template<typename Iterator,typename Compare>
Iterator my_find_if(Iterator first, Iterator last, Compare com)
{
	for (; first != last; first++)
	{
		if (com(*first)) // com.operator()(*first)
			return first;
	}
	return last;
}

// 实现_mybind1st
template<typename Compare,typename T>
class _mybind1st
{
public:
	_mybind1st(Compare comp, T val)
		:_comp(comp)
		,_val(val)
	{}
	// bind1st(greater<int>(), 60)
	bool operator()(const T& second)
	{
		return _comp(_val, second); // 底层还是二元函数对象来作用的 greater
	}
private:
	Compare _comp;
	T _val;
};

// 模拟实现bind1st
template<typename Compare,typename T>
_mybind1st<Compare,T> mybind1st(Compare com, const T& val)
{
	// 返回的是一元函数对象
	return _mybind1st<Compare,T>(com, val);
}

// 实现_mybind2nd
template<typename Compare, typename T>
class _mybind2nd
{
public:
	_mybind2nd(Compare comp, T val)
		:_comp(comp)
		, _val(val)
	{}
	// bind1st(greater<int>(), 60)
	bool operator()(const T& first)
	{
		return _comp(first, _val); // 底层还是二元函数对象来作用的 greater
	}
private:
	Compare _comp;
	T _val;
};

// 模拟实现bind2nd
template<typename Compare, typename T>
_mybind2nd<Compare, T> mybind2nd(Compare com, const T& val)
{
	// 返回的是一元函数对象
	return _mybind2nd<Compare, T>(com, val);
}

int main()
{
	vector<int> vec;
	srand(time(nullptr));
	for (int i = 0; i < 10; i++)
	{
		vec.push_back(rand() % 100 + 1);
	}

	showContainer(vec);
	sort(vec.begin(), vec.end()); // 默认从小到大排序
	showContainer(vec);

	// greater 二元函数对象
	sort(vec.begin(), vec.end(), greater<int>()); // 从大到小排序,传入函数对象
	showContainer(vec);

	// 将60按顺序插入到vector中 在vector中找第一个小于60的元素
	auto it1 = my_find_if(vec.begin(), vec.end(), mybind1st(greater<int>(), 60));
	if (it1 != vec.end())
	{
		vec.insert(it1, 60);
	}
	showContainer(vec);
	auto it2 = my_find_if(vec.begin(), vec.end(), mybind2nd(less<int>(), 60));
	if (it2 != vec.end())
	{
		vec.insert(it2, 60);
	}
	showContainer(vec);

	return 0;
}

bind1st或bind2nd绑定二元函数对象,可以生成一个一元函数对象,一元函数对象底层还是二元函数对象来实现的。

bind1st和bind2nd应用在在编程中如果想使用一个一元函数对象,但是库中没有,只有二元函数对象,我们就可以通过绑定器绑定二元函数对象的某一个参数,进而将它变成一个一元函数对象进而来使用。

bind1st和bind2nd有一个非常大的局限性,只能绑定二元函数对象,但是实际中,使用到的参数可能是三个或者以上,所以C++11就从Boost库中引入了更强大的bind绑定器。

C++11bind

std::bind是一个函数模板,可以看作是一个函数绑定器(适配器),它允许将一个可调用对象和其部分或全部参数进行绑定,生成一个新的可调用对象。这个新的可调用对象可以在后续的代码中被调用,并且可以省略掉已经绑定的参数。

// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2) 
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

简单演示一下std::bind的使用方法:

#include <iostream>
#include <functional>
using namespace std;
 
int add(int a, int b)
{
    return a + b;
}
struct Adder
{
    int operator()(int a, int b)
    {
        return a + b;
    }
};
class Calculator
{
public:
    int add(int a, int b)
    {
        return a + b;
    }
};
int main() {
    // 1、绑定普通函数
    // 使用 std::bind 绑定 add 函数和它的参数
    auto addFunc1 = std::bind(add, 3, 5);
    // 调用绑定后的可调用对象
    cout << addFunc1() << endl; //就相当于调用 add(3,5)
 
    // 使用占位符预留参数位置
    auto addFunc2 = std::bind(add, std::placeholders::_1, std::placeholders::_2);
    cout << addFunc2(3, 5) << endl;
    // 混合使用占位符和参数
    auto addFunc3 = std::bind(add, std::placeholders::_1, 5);
    cout << addFunc3(3) << endl;
    // 参数调换顺序
    auto addFunc4 = std::bind(add, std::placeholders::_2, std::placeholders::_1);
    cout << addFunc4(3, 5) << endl;
 
    // 2、绑定函数对象
    Adder adder;
    auto addFunc5 = std::bind(Adder(), std::placeholders::_1, std::placeholders::_2);
    cout << addFunc5(2, 2) << endl;
    auto addFunc6 = std::bind(adder, std::placeholders::_1, std::placeholders::_2);
    cout << addFunc6(2, 2) << endl;//相当于调用adder(1,2)
 
    // 3、绑定lambda表达式
    auto addFunc7 = std::bind([](int a, int b) {return a + b; }, std::placeholders::_1, std::placeholders::_2);
    cout << addFunc7(3, 3) << endl;
 
    // 2、绑定成员函数
    Calculator calc;
    auto addFunc8 = std::bind(&Calculator::add, &calc, std::placeholders::_1, std::placeholders::_2);
    cout << addFunc8(4, 4) << endl;//相当于cal.add(1,2)
 
    return 0;
}

bind绑定器,函数对象,Lambda表达式都只能在一条语句中使用,那么如果想在其他地方使用,该如何使用呢?可不可以留下函数对象的类型呢?C++11就引入了function。

C++11function

function是一个强大的包装器,可以存储、赋值任何可调用函数,包括普通函数、函数指针、成员函数指针、函数对象(仿函数)以及 lambda 表达式。它就像一个容器,能将不同类型的可调用对象统一封装起来,以统一的方式进行调用。

function本质是一个类模板,也是一个包装器。


std::function在头文件<functional>
// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参

我们先来看下function的简单应用:

#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<functional>
#include<ctime>

using namespace std;

void hello1()
{
	cout << "hello1 world!" << endl;
}
void hello2(string str)
{
	cout << str << endl;
}
int sum(int a, int b)
{
	return a + b;
}
class Test
{
public:
	void hello(string str)
	{
		cout << str << endl;
	}
};
int main()
{
	// 使用函数类型实例化function
	// 包装普通函数
	function<void()> func1 = hello1;
	func1(); // func1.operator()() => hello1()

	function<void(string)> func2 = hello2;
	func2("hello2 world!"); // func2.operator()() => hello2()
	
	function<int(int, int)> func3 = sum;
	cout << func3(10, 20) << endl; // func3.operator()() => sum()

	// 包装成员函数
	Test test;
	function<void(Test*, string)> func4 = &Test::hello;
	func4(&test, "Test::hello world!");

	// 包装函数对象
	function<int(int, int)> func5 = [](int a, int b)->int {return a + b; };
	cout << func5(100, 200) << endl; // operator()

	return 0;
}

  1. 使用函数类型实例化function
  2. 通过function调用operator()函数的时候,需要根据函数类型传入相应参数

但是上面的例子,好像并没有看出function的好处,还不如直接调用函数呢,下面我们实现段代码看一下使用function的好处。


#include<iostream>
#include<vector>
#include<string>
#include<map>
#include<algorithm>
#include<functional>
#include<ctime>

using namespace std;

void doShowAllBooks() { cout << "查看所有书籍" << endl; }
void doBack() { cout << "还书" << endl; }
void borrow() { cout << "借书" << endl; }
void doQueryBooks() { cout << "查询书籍信息" << endl; }
void dologinOut() { cout << "注销" << endl; }

int main()
{
	int choice;

	map<int, function<void()>> actionMap;

	actionMap.insert({ 1, doShowAllBooks });
	actionMap.insert({ 2, doBack });
	actionMap.insert({ 3, borrow });
	actionMap.insert({ 4, doQueryBooks });
	actionMap.insert({ 5, dologinOut });

	for (;;)
	{
		cout << "-----------------" << endl;

		cout << "查看所有书籍" << endl;
		cout << "还书" << endl;
		cout << "借书" << endl;
		cout << "查询书籍" << endl;
		cout << "注销" << endl;
		cout << "-----------------" << endl;

		cin >> choice;
		auto it = actionMap.find(choice);
		if (it == actionMap.end())
		{
			cout << "输入数字无效,请重新选择!" << endl;
		}
		else
		{
			it->second();
		}
	}

		// 不实用,这段代码无法闭合,无法做到“开-闭”原则
		/*switch (choice)
		{
		case 1:
			cout << "查看书籍" << endl;
			break;
		case 2:
			cout << "还书" << endl;
			break;
		case 3:
			cout << "借书" << endl;
			break;
		case 4:
			cout << "查询" << endl;
			break;
		case 5:
			cout << "注销" << endl;
			break;
		default:
			break;
		}*/
	return 0;
}

function函数对象类型的实现原理


#include<iostream>
#include<string>
#include<functional>
using namespace std;

void hello(string str)
{
	cout << str << endl;
}
int sum(int a, int b)
{
	return a + b;
}

// 通用模板,作为偏特化的基础
template<typename Fty>
class myfunction {};

/*
// 模板偏特化允许针对特定的模板参数类型或者参数组合,提供专门的实现
template<typename R, typename A1>
class myfunction<R(A1)>
{
public:
	using PFUNC = R(*)(A1);
	myfunction(PFUNC pfunc)
		:_pfunc(pfunc)
	{}
	R operator()(A1 arg)
	{
		return _pfunc(arg); // hello("hello world!")
	}
private:
	PFUNC _pfunc;
};
template<typename R, typename A1, typename A2>
class myfunction<R(A1, A2)>
{
public:
	using PFUNC = R(*)(A1, A2);
	myfunction(PFUNC pfunc)
		:_pfunc(pfunc)
	{}
	R operator()(A1 arg1, A2 arg2)
	{
		return _pfunc(arg1, arg2); // hello("hello world!")
	}
private:
	PFUNC _pfunc;
};
*/

// 函数参数不确定,难道要特化很多模板出来吗?不需要,C++11提供了可变参数模板
template<typename R, typename... A>
class myfunction<R(A...)>
{
public:
	using PFUNC = R(*)(A...);
	myfunction(PFUNC pfunc)
		:_pfunc(pfunc)
	{}
	R operator()(A... arg)
	{
		return _pfunc(arg...); // hello("hello world!") sum(10,20)
	}
private:
	PFUNC _pfunc;
};


int main()
{
	myfunction<void(string)> func1(hello);
	func1("hello world!"); // func1.operator()("hello world!")
	
	myfunction<int(int, int)> func2(sum);
	cout << func2(10, 20) << endl; //func2.operator()(10,20)
	return 0;
}

function其实就是一个函数对象(重载了operator()函数),底层封装了一个函数指针,指向了要包装的函数,通过可变参数模板,operator()()可以接收类型及任意数量的参数,将参数传递给内部,内部使用函数指针进行函数的调用。

bind与function实现线程池

#include<iostream>
#include<thread>
#include<vector>
#include<functional>

class Thread
{
public:
	Thread(std::function<void()> func)
		: _func(func)
	{
	}
	~Thread() {}
	std::thread start()
	{
		std::thread t(_func);
		return  t;
	}
private:
	std::function<void()> _func;
};
class ThreadPool
{
public:
	void startPool(int size)
	{
		for (int i = 0; i < size; i++)
		{
			// 使用bind绑定器绑定线程函数
			_pool.push_back(std::make_unique<Thread>(std::bind(&ThreadPool::runInThread, this, i)));
			
			// 使用Lambda代替bind 
			// auto Func = [this, i](){ runInThread(i); };
			// _pool.push_back(std::make_unique<Thread>(Func));
		}
		for (int i = 0; i < size; i++)
		{
			_handler.push_back(_pool[i]->start());
		}
		for (std::thread& t : _handler)
		{
			t.join();
		}
	}
private:
	std::vector<std::unique_ptr<Thread>> _pool; // 线程池
	std::vector<std::thread> _handler; // 线程列表
	// 线程执行的方法
	void runInThread(int id)
	{
		std::cout << "call runInThread! id:" << id << std::endl;
	}
};

int main()
{
	ThreadPool pool;
	pool.startPool(10);
	return 0;
}

总结:

std::function和std::bind虽然都与可调用对象相关,但它们的功能和用途各有侧重。std::function主要用于存储和调用可调用对象,实现回调和多态;std::bind主要用于参数绑定和调整参数顺序,简化函数调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值