c/c++语言开发共享C++11中bind绑定器和function函数对象介绍

一. bind1st和bind2nd1.c++ stl中的绑定器bind1st:operator()的第一个形参变量绑定成一个确定的值bind2nd:operator()的第二个形参变量绑定成一个确定

一. bind1st和bind2nd

1.c++ stl中的绑定器

  • bind1st:operator()的第一个形参变量绑定成一个确定的值
  • bind2nd:operator()的第二个形参变量绑定成一个确定的值

c++11从boost库中引入了bind绑定器和function函数对象机制

bind可用于给多元函数降元:bind + 二元函数对象 = 一元函数对象

#include<iostream>  #include<vector>  #include<functional>  #include<algorithm>//泛型算法  #include<ctime>  using namespace std;  template<typename container>  void showcontainer(container& con)  {  	//typename container::iterator it=con.begin();  	auto 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 < 20; ++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);    	/*  	把70按顺序插入到vec容器中 ->找第一个小于70的数字  	operator()(const t &val)  	greater a>b  	less a<b  	绑定器+二元函数对象=》一元函数对象  	bind1st:+greater bool operator()(70,const_ty&_right)  	bind2nd:+less bool operator()(const_ty &_left,70)  	*/  	auto it1 = find_if(vec.begin(), vec.end(), bind1st(greater<int>(), 70));  	if (it1 != vec.end())  	{  		vec.insert(it1, 70);  	}  	showcontainer(vec);  	return 0;  }

2.bind1st和bind2nd的底层原理实现

绑定器本身是一个函数对象

#include<iostream>  #include<vector>  #include<functional>  #include<algorithm>  #include<ctime>  using namespace std;  template<typename container>  void showcontainer(container& con)  {  	auto it = con.begin();  	for (; it != con.end(); ++it)  	{  		cout << *it << " ";  	}  	cout << endl;  }  //遍历两个迭代器之间的元素,如果满足函数对象的运算,就返回当前的迭代器,如果都不满足就返回end  template<typename iterator,typename compare>  iterator my_find_if(iterator first, iterator last, compare comp)  {  	//这里传入的comp是封装好的一元函数对象  	for (; first != last; ++first)  	{  		if (comp(*first))//获取容器的一个元素  		{  			return first;  		}  	}  	return last;  }  template<typename compare,typename t>  class _mybind1st//绑定器是函数对象的一个应用  {  public:  	//这里传入的comp是二元函数对象  	_mybind1st(compare comp,t val)  		:_comp(comp),_val(val){}  	//通过重载operator()把二元函数对象封装为一元函数对象  	bool operator()(const t& second)  	{  		return _comp(_val, second);  	}  private:  	compare _comp;  	t _val;  };  template<typename compare,typename t>  _mybind1st<compare, t> mybind1st(compare comp, const t& val)  {  	//直接使用函数模板,好处是可以进行类型的推演  	//这里传入的comp是一个二元函数对象  	//通过二元函数对象构造一元函数对象  	//绑定器本身是一个函数对象,也就是重载了operator()  	return _mybind1st<compare, t>(comp, val);  }  int main()  {  	vector<int> vec;  	srand(time(nullptr));  	for (int i = 0; i < 20; ++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);    	auto it1 = my_find_if(vec.begin(), vec.end(), mybind1st(greater<int>(), 70));  	if (it1 != vec.end())  	{  		vec.insert(it1, 70);  	}  	showcontainer(vec);    	return 0;  }

二. 模板的完全特例化和非完全特例化

有完全特例化优先匹配完全特例化,有部分特例化就匹配部分特例化,没有的话就从原模板自己实例化

#include<iostream>  using namespace std;  template<typename t>  class vector  {  public:  	vector() { cout << "call vector template init" << endl; }  };  //对char*类型提供完全特例化版本  template<>  class vector<char*>  {  public:  	vector() { cout << "call vector<char*> init" << endl; }  };  //对指针类型提供的部分特例化版本(部分:只知道是个指针,但是指针的类型是什么不知道)  template<typename ty>  class vector<ty*>  {  public:  	vector() { cout << "call vector<ty*> init" << endl; }  };  //指针函数指针(有返回值,有两个形参变量)提供的部分特例化  template<typename r,typename a1,typename a2>  class vector<r(*)(a1, a2)>  {  public:  	vector() { cout << "call vector<r(*)(a1,a2)> init" << endl; }  };  //针对函数(有一个返回值,有两个形参变量)类型提供的部分特例化  template<typename r, typename a1, typename a2>  class vector<r(a1, a2)>  {  public:  	vector() { cout << "call vector<r(a1,a2)> init" << endl; }  };  int sum(int a, int b) { return a + b; }  int main()  {  	vector<int> vec1;  	vector<char*> vec2;  	vector<int*> vec3;  	vector<int(*)(int, int)> vec4;  	vector<int(int, int)> vec5;    	//注意区分函数类型和函数指针类型  	typedef int(*pfunc1)(int, int);  	pfunc1 pfunc1 = sum;  	cout << pfunc1(10, 20) << endl;    	typedef int pfunc2(int, int);  	pfunc2* pfunc2 = sum;  	cout << (*pfunc2)(10, 20) << endl;  	return 0;  }

C++11中bind绑定器和function函数对象介绍

#include<iostream>  #include<typeinfo>  using namespace std;  //t包含了所有大的类型  template<typename t>  void func(t a)  {  	cout << typeid(t).name() << endl;  }  int sum(int a, int b) { return a + b; }    //把所有形参类型都取出来  template<typename r, typename a1, typename a2>  void func2(r(*a)(a1, a2))  {  	cout << typeid(r).name() << endl;  	cout << typeid(a1).name() << endl;  	cout << typeid(a2).name() << endl;  }  template<typename r,typename t,typename a1,typename a2>  void func3(r(t::*a)(a1, a2))  {  	cout << typeid(r).name() << endl;  	cout << typeid(t).name() << endl;  	cout << typeid(a1).name() << endl;  	cout << typeid(a2).name() << endl;  }  class test  {  public:  	int sum(int a, int b) { return a + b; }  };  int main()  {  	//func(10);//int  	//func("aaa");//const char *  	func(sum);  	func2(sum);  	func3(&test::sum);  	return 0;  }

C++11中bind绑定器和function函数对象介绍

三. function函数对象

绑定器,函数对象,lambda表达式本质上都是函数对象,只能使用在一条语句中,但是如果想要在多条语句中使用,就需要function

使用function函数需要注意:

  • 函数类型实例化function;
  • 通过function调用operator()函数的时候,需要根据函数类型传入相应的参数
#include<iostream>  #include<functional>  using namespace std;  void hello1()  {  	cout << "hello world!" << endl;  }  void hello2(string str)  {  	cout << str << endl;  }  int sum(int a, int b)  {  	return a + b;  }  int main()  {  	//从function的模板定义处,看到希望用一个函数类型实例化function  	function<void()> func1 = hello1;  	func1();//func1.operator() => hello1()    	function<void(string)> func2 = hello2;  	func2("hello hello2!");    	function<int(int, int)> func3 = sum;  	cout << func3(2, 3) << endl;    	function<int(int, int)> func4 = [](int a, int b)->int {return a + b; };  	cout << func4(3, 4) << endl;  	return 0;  }

C++11中bind绑定器和function函数对象介绍

function不仅可以留下普通全局函数的类型,对于类的成员方法也可以进行类型保留:

#include<iostream>  #include<functional>  using namespace std;    class test  {  public://必须依赖一个对象void(test::*pfunc)(string)  	void hello(string str) { cout << str << endl; }  };  int main()  {  	//成员方法一经编译都会多一个当前类型的this指针  	function<void (test*, string)> func = &test::hello;  	test t;  	//对于成员方法的调用需要依赖一个成员对象  	func(&t, "call test::hello!");  	return 0;  }  

function的特点:可以把所有函数、绑定器、函数对象和lambda表达式的类型保留起来,在其他地方都可以使用。否则绑定器、lambda表达式就只能使用在语句中。

#include<iostream>  #include<functional>  #include<map>  using namespace std;  void doshowallbooks(){ cout << "查看所有书籍信息" << endl; }  void doborrow() { cout << "借书" << endl; }  void doback() { cout << "还书" << endl; }  void doquerybooks() { cout << "查询书籍" << endl; }  void dologinout() { cout << "注销" << endl; }  int main()  {  	int choice = 0;  	//使用function函数对象完成  	map<int, function<void()>> actionmap;  	actionmap.insert({ 1,doshowallbooks });  	actionmap.insert({ 2,doborrow });  	actionmap.insert({ 3,doback });  	actionmap.insert({ 4,doquerybooks });  	actionmap.insert({ 5,dologinout });  	for (;;)  	{  		cout << "------------------" << endl;  		cout << "1.查看所有书籍信息" << endl;  		cout << "2.借书" << endl;  		cout << "3.还书" << endl;  		cout << "4.查询书籍" << endl;  		cout << "5.注销" << endl;  		cout << "------------------" << endl;  		cout << "请选择:";  		cin >> choice;    		auto it = actionmap.find(choice);  		if (it == actionmap.end())  		{  			cout << "输入数字无效,重新选择" << endl;  		}  		else  		{  			it->second();  		}  		//不好,因为这块代码无法闭合,无法做到“开-闭”原则,也就是说这块代码随着需求的更改需要一直改,永远也闭合不了,避免不了要产生很多问题  		/*  		switch(choice)  		{  			case 1:break;  			case 2:break;  			case 3:break;  			case 4:break;  			case 5:break;  			default:break;  		}  		*/  	}  	return 0;  }

function的实现原理:

#include<iostream>  #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:  	//typedef r(*pfunc)(a1);  	using pfunc = r(*)(a1);  	myfunction(pfunc pfunc):_pfunc(pfunc){}  	r operator()(a1 arg)  	{  		return _pfunc(arg);  	}  private:  	pfunc _pfunc;  };    template<typename r, typename a1,typename a2>  class myfunction<r(a1,a2)>  {  public:  	//typedef r(*pfunc)(a1);  	using pfunc = r(*)(a1,a2);  	myfunction(pfunc pfunc) :_pfunc(pfunc) {}  	r operator()(a1 arg1,a2 arg2)  	{  		return _pfunc(arg1,arg2);  	}  private:  	pfunc _pfunc;  };  */  //...表示可变参,a表示的是一组1类型,个数任意  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...);  	}  private:  	pfunc _pfunc;  };  int main()  {  	myfunction<void(string)> func1(hello);  	func1("hello world");  	myfunction<int(int, int)> func2(sum);  	cout << func2(10, 20) << endl;  	return 0;  }

四. bind和function实现线程池

#include<iostream>  #include<functional>  using namespace std;  using namespace placeholders;      //c++11 bind 绑定器=>返回的结果是一个函数对象  void hello(string str) { cout << str << endl; }  int sum(int a, int b) { return a + b; }  class test  {  public:  	int sum(int a, int b) { return a + b; }  };    int main()  {  	//bind是函数模板,可以自动推演模板类型参数  	bind(hello, "hello bind!")();  	cout << bind(sum, 20, 30)() << endl;  	cout << bind(&test::sum, test(), 20, 30)() << endl;  	  	//function只接受一个类型,绑定器可以给相应的函数绑定固定的参数,绑定器只能使用在语句当中  	//参数占位符,绑定器出了语句,无法继续使用  	bind(hello, _1)("hello bind 2");  	cout << bind(sum, _1, _2)(20, 30) << endl;  	  	//此处把bind返回的绑定器binder就复用起来了  	function<void(string)> func1 = bind(hello, _1);  	func1("hello china!");  	func1("hello shan xi!");  	func1("hello da li!");    }
#include<iostream>  #include<functional>  #include<thread>  #include<vector>  using namespace std;  using namespace placeholders;    //线程类  class thread  {  public:  	thread(function<void(int)> func,int no):_func(func),_no(no){}  	thread start()  	{  		thread t(_func,_no);  		return t;  	}  private:  	function<void(int)> _func;  	int _no;  };  //线程池类  class threadpool  {  public:  	threadpool(){}  	~threadpool()  	{  		//释放thread对象占用的堆资源  		for (int i = 0; i < _pool.size(); i++)  		{  			delete _pool[i];  		}  	}  	//开启线程池  	void startpool(int size)  	{  		for (int i = 0; i < size; i++)  		{  			//不管是c++里面的thread还是linux里面的pthread需要的线程函数都是一个c函数,是不能够使用成员方法的,因为它是c的函数类型,不可能把成员方法的函数指针给一个c的函数指针,接收不了。所以就需要绑定,把runinthread所依赖的参数全部绑定  			_pool.push_back(new thread(bind(&threadpool::runinthread, this, _1),i));  		}  		for (int i = 0; i < size; i++)  		{  			_handler.push_back(_pool[i]->start());  		}  		for (thread& t : _handler)  		{  			t.join();  		}  	}  private:  	vector<thread*> _pool;  	vector<thread> _handler;  	//把runinthread这个成员方法充当线程函数  	void runinthread(int id)  	{  		cout << "call runinthread! id:" << id << endl;  	}  };  int main()  {  	threadpool pool;  	pool.startpool(10);  	return 0;  }

五. lambda表达式

  • 函数对象的应用:使用在泛型算法参数传递、比较性质、自定义操作、优先级队列和智能指针
  • 函数对象的缺点:需要先定义一个函数对象类型,但是类型定义完后可能只是用在了定义的地方,后面可能不会再用了,没有必要为了需要一个函数对象定义一个类型,这个类型就永远在代码当中。

c++11函数对象的升级版 => lambda表达式:

  • lambda表达式:底层依赖函数对象的机制实现的
  • lambda表达式语法:[捕获外部变量](形参列表) ->返回值{操作代码};

如果lambda表达式的返回值不需要,那么“->返回值”可以省略

[捕获外部变量]

  • [ ]:表示不捕获任何外部变量
  • [=]:以传值的方式捕获外部的所有变量
  • [&]:以传引用的方式捕获外部的所有变量[this]:捕获外部的this指针
  • [=,&a]:以传值的方式捕获外部的所有变量,但是a变量以传引用的方式捕获
  • [a,b]:以传值的方式捕获外部变量a和b
  • [a,&b]:a以值传递捕获,b以传引用的方式捕获

1.lambda表达式的实现原理

#include<iostream>  using namespace std;  template<typename t=void>  class testlambda01  {  public:  	void operator()()  	{  		cout << "hello world" << endl;  	}  };  template<typename t = int>  class testlambda02  {  public:  	testlambda02() {}  	int operator()(int a, int b)  	{  		return a + b;  	}  };  template<typename t = int>  class testlambda03  {  public:  	testlambda03(int a,int b):ma(a),mb(b){}  	void operator()()const  	{  		int tmp = ma;  		ma = mb;  		mb = tmp;  	}  private:  	mutable int ma;  	mutable int mb;  };  class testlambda04  {  public:  	testlambda04(int &a,int &b):ma(a),mb(b){}  	void operator()()const  	{  		int tmp = ma;  		ma = mb;  		mb = tmp;  	}  private:  	int& ma;  	int& mb;  };  int main()  {  	auto func1 = []()->void {cout << "hello world" << endl; };  	func1();    	auto func2 = [](int a, int b)->int {return a + b; };  	cout << func2(20, 30) << endl;    	int a = 10;  	int b = 20;  	//按值传递a,b值未被改变  	auto func3 = [a, b]()mutable  	{  		int tmp = a;  		a = b;  		b = tmp;  	};  	func3();  	cout << "a:" << a << " b:" << b << endl;  	  	//传引用值a,b值被改变  	auto func4 = [&]()  	{  		int tmp = a;  		a = b;  		b = tmp;  	};  	func4();  	cout << "a:" << a << " b:" << b << endl;    	cout << "--------------------" << endl;  	testlambda01<> t1;  	t1();  	testlambda02<> t2;  	cout << t2(20, 30) << endl;  	testlambda03<> t3(a,b);  	t3();  	cout << "a:" << a << " b:" << b << endl;  	testlambda04 t4(a,b);  	t4();  	cout << "a:" << a << " b:" << b << endl;  	  	return 0;  }

C++11中bind绑定器和function函数对象介绍

mutable:成员变量本身也不是常量,只不过在常方法中this指针被修饰成const,在声明成员变量前加mutable,可以在const方法中修改普通的成员变量

lambda表达式后面修饰mutable相当于在它的所有成员变量添加一个mutable修饰。

2.lambda表达式的应用实践

lambda表达式应用于泛型算法:

#include<iostream>  #include<vector>  #include<algorithm>  using namespace std;    int main()  {  	vector<int> vec;  	for (int i = 0; i < 20; ++i)  	{  		vec.push_back(rand() % 100 + 1);  	}  	sort(vec.begin(), vec.end(),  		[](int a, int b)->bool  		{  			return a > b;  		});  	for (int val : vec)  	{  		cout << val << " ";  	}  	cout << endl;  	//65按序插入序列 要找一个小于65的数字  	auto it = find_if(vec.begin(), vec.end(),  		[](int a)->bool {return a < 65; });  	if (it != vec.end())  	{  		vec.insert(it, 65);  	}  	for (int val : vec)  	{  		cout << val << " ";  	}  	cout << endl;  	for_each(vec.begin(), vec.end(), [](int a)  	{  		if (a % 2 == 0)  			cout << a << " ";  	});  	cout << endl;  	return 0;  }

既然lambda表达式只能使用在语句中,如果想跨语句使用之前定义好的lambda表达式,采用function类型来表示函数对象的类型。

哈希表的应用:

#include<iostream>  #include<vector>  #include<map>  #include<functional>  using namespace std;  int main()  {  	//auto只能出现在根据右边表达式推导左边的类型,只能使用在函数的局部作用域的范围之内  	//预先lambda表达式不知道需要先存储lambda表达式类型  	map<int, function<int(int, int)>> caculatemap;  	caculatemap[1] = [](int a, int b)->int {return a + b; };  	caculatemap[2] = [](int a, int b)->int {return a - b; };  	caculatemap[3] = [](int a, int b)->int {return a * b; };  	caculatemap[4] = [](int a, int b)->int {return a / b; };    	cout << "请选择";  	int choice;  	cin >> choice;  	cout << caculatemap[choice](10, 15) << endl;  	return 0;  }

智能指针自定义删除器:

#include<iostream>  #include<vector>  #include<functional>  using namespace std;  int main()  {  	unique_ptr<file, function<void(file*)>>  		ptr1(fopen("data.txt", "w"), [](file* pf) {fclose(pf); });  }  

传入函数对象使得容器元素按照指定方式排列:

#include<iostream>  #include<vector>  #include<functional>  #include <queue>  using namespace std;    class data  {  public:  	data(int val1=10,int val2=10):ma(val1),mb(val2){}  	int ma;  	int mb;  };  int main()  {  	//优先级队列  	//priority_queue<data> queue;  	using func = function<bool(data&, data&)>;  	priority_queue<data, vector<data>, func>  		maxheap([](data& d1, data& d2)->bool  			{  				return d1.mb > d2.mb;  			});  	maxheap.push(data(10, 20));  	maxheap.push(data(15, 15));  	maxheap.push(data(20, 10));  }

到此这篇关于c++11中bind绑定器和function函数对象介绍的文章就介绍到这了,更多相关c++bind绑定器内容请搜索<计算机技术网(www.ctvol.com)!!>以前的文章或继续浏览下面的相关文章希望大家以后多多支持<计算机技术网(www.ctvol.com)!!>!

需要了解更多c/c++开发分享C++11中bind绑定器和function函数对象介绍,都可以关注C/C++技术分享栏目—计算机技术网(www.ctvol.com)!

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/1156154.html

(0)
上一篇 2022年7月21日
下一篇 2022年7月21日

精彩推荐