C++11 学习笔记 std::function和bind绑定器

这是C++11增加的,不仅让我们使用标准库函数时变得更加方便,而且还能方便地实现延迟求值。

1.1 可调用对象(Callable Objects)

可调用对象有如下几种:

  • 函数指针
  • 具有operator()成员函数的类对象(仿函数)
  • 可被转换为函数指针的类对象
  • 类成员(函数)指针

以上涉及的对象可以像一个函数一样做调用操作,统称为可调用对象。现在,C++11通过提供std::function和std::bind统一了可调用对象的各种操作。

1.2 可调用对象包装器——std::function

      C++中的可调用对象虽然具有比较统一操作形式(除了类成员指针之外,都是后面加括号进行调用),但定义方法五花八门。为了统一泛化函数对象,函数指针,引用函数,成员函数的指针的各种操作,让我们可以按更统一的方式写出更加泛化的代码,C++11推出了std::function。

  std::function是可调用对象的包装器。它是一个类模板,可以容纳除了类成员(函数)指针之外的所有可调用对象。通过指定它的模板参数,它可以用统一的方式处理函数,函数对象,函数指针,并允许保存和延迟执行它们。

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

void func(void) 
{
	cout << __FUNCTION__ << "(" << ") ->: " << endl;
}

class Foo
{
public:
	static int foo_func(int a) {
		cout << __FUNCTION__ << "(" << a << ") ->: ";
		return a;
	}
};

class Bar
{
public:
	int operator()(int a) {
		cout << __FUNCTION__ << "(" << a << ") ->: ";
		return a;
	}
};

int main() {
	//绑定一个普通函数
	std::function<void(void)> fr1 = func;
	fr1();

	//绑定一个类的静态成员函数
	std::function<int(int)> fr2 = Foo::foo_func;
	cout << fr2(123) << endl;

	//绑定一个仿函数
	Bar bar;
	fr2 = bar;
	cout << fr2(123) << endl;

	return 0;
}

std::function的使用方法:我们给std::function填入合适的函数签名(即一个函数类型,只需要包括返回值和参数表)之后,它就变成了一个可以容纳所有这一类调用方式的“函数包装器”。

 #include <iostream>
 #include <functional>
 
 using namespace std;
 
 class A
 {
 public:
     A(const std::function<void()>& f){
         :callback_(f){}
     
     void notify(void){
         callback_();
     }
 private:
     std::function<void()> callback_;
 };
 
 class Foo
 {
 public:
     void operator()(void){
         cout << __FUNCTION__<< endl;
     }
 };
 
 int main(){
     Foo foo;
     A aa(foo);
     aa.notify();
     
     return 0;
 }

从上面的例子看,std::function可以取代函数指针的作用。因为它可以保存函数延迟执行,所以比较适合作为回调函数,也可以把它看做类似于C#中特殊的委托(只有一个成员的委托)。

#include <iostream>
#include <functional>

using namespace std;

void call_when_even(int x, const std::function<void(int)>& f) {
	if (!(x & 1)) {
		f(x);
	}
}

void output(int x) {
	cout << x << " ";
}

int main(void) {
	for (int i = 0; i<10; i++) {
		call_when_even(i, output);
	}
	cout << endl;

	return 0;
}

std::function还可以作为函数入参,这样可以在函数外部控制函数的内部行为了,让我们的函数变得更加灵活。

C++11 std::function 是一种通用、多态的函数封装,它的实例可以对任何可以调用的目标实体进行存储、复制和调用操作,它也是对 C++中现有的可调用实体的一种类型安全的包裹(相对来说,函数指针的调用不是类型安全的),换句话说,就是函数的容器。当我们有了函数的容器之后便能够更加方便的将函数、函数指针作为对象进行处理。例如:

#include <functional>
#include <iostream>
 
int add(int a,int b) {
	return a + b;
}
 
//使用函数对象包装器作为参数,改变模板的默认行为
template<typename T, typename F>
T calc(T a, T b, F f)
{
	return f(a, b);
}
 
int main() {
	// std::function 包装了一个返回值为 int, 参数为 int 的普通函数add
	std::function<int(int,int)> func = add;
	
	
	// std::function 包装了一个返回值为 int, 参数为 int 的lambda函数
	std::function<int(int,int)> func2 = [&](int a,int b) -> int {
		return a - b;
	};
 
	//使用例子:
	//1.直接使用函数包装器
	std::cout << func(10,10) << std::endl;
	std::cout << func2(10,10) << std::endl;
 
	//2.将函数包装器作为参数使用
	std::cout << calc(10, 10,func2) << std::endl;
	return 0;
}

1.3 std::bind绑定器

  std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候。通俗来讲,它主要有两大作用:

  1).将可调用对象与其参数一起绑定成一个仿函数。

  2).将多元(参数个数为n,n>1)可调用对象转成一元或者(n-1)元可调用对象,即只绑定部分参数。

  function模板类和bind模板函数,都可以实现类似函数指针的功能,但却却比函数指针更加灵活,特别是函数指向类的非静态成员函数时。

  1).std::function可以绑定到全局函数/类静态成员函数(类静态成员函数与全局函数没有区别)。

  2).绑定到类的非静态成员函数,则需要使用std::bind。

"std::placeholders::_1"是一个占位符对象,用于表示当函数output(output_add_2)通过函数fr进行调用时,函数fr的第一个参数在函数output(output_add_2)的参数列表中的位置。

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

void call_when_even(int x, const function<void(int)>& f)
{
	if (!(x & 1))
	{
		f(x);
	}
}

void output(int x)
{
	cout << x << " ";
}

void output_add_2(int x)
{
	cout << x + 2 << " ";
}

int main()
{
	{
		//auto fr保存bind返回结果,实际上bind返回类型是一个STL内部定义的仿函数指针,
		//可以直接赋值给function
		//placeholders::_1是一个占位符,代表这个位置在函数调用后,并第一个参数(参数0)所替代。
		//参考C语言中的%d
		auto fr = bind(output, placeholders::_1);
		for (int i = 0; i<10; i++) {
			call_when_even(i, fr);
		}
		cout << endl;
	}

	{
		auto fr = bind(output_add_2, placeholders::_1);
		for (int i = 0; i<10; i++) {
			call_when_even(i, fr);
		}
		cout << endl;
	}

	return 0;
}

  下面是两个样例:

 #include <iostream>
 #include <functional>
 
 using namespace std;
 
 class A
 {
 public:
     int i_=0;
 
     void output(int x, int y){
         cout << x << " " << y <<endl;
     }
 };
 
 int main(){
     A a;
     std::function<void(int, int)> fr = std::bind(&A::output, &a, std::placeholders::_1,std::placeholders::_2);
     fr(1,2);
 
     std::function<int&(void)> fr_i = std::bind(&A::i_, &a);
     fr_i() = 123;
     cout << a.i_ << endl;
 
     return 0;
 }
 //使用组合bind函数,找出集合中大于5小于10的元素个数
 #include <iostream>
 #include <functional>
 
 using namespace std;
 
 auto f = std::bind(std::logical_and<bool>(),std::bind(std::greater<int>(),_1,5),std::bind(std::less_equal<int>(),  _1, 10));
 
 int main(){
      set<int> se={1,2,3,4,5,6,7,8,9};
     int count = std::count_if(se.begin(), se.end(), f);
     cout << count <<endl;
     
     return 0;
 }

std::bind需要注意的一些事项:(http://www.cnblogs.com/slysky/p/3822640.html)

1).std::bind预先绑定的参数需要传具体的变量或值进去,对于预先绑定的参数,是pass-by-value的

2).对于不事先绑定的参数,需要传std::placeholders进去,从_1开始,依次递增。placeholder是pass-by-reference的

3).bind的返回值是可调用实体,可以直接赋给std::function对象

4).对于绑定的指针、引用类型的参数,使用者需要保证在可调用实体调用之前,这些参数是可用的

5).类的this可以通过对象或者指针来绑定

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值