std::function和std::bind绑定器

C++11中std::function and std::bind

1. 可调用对象

C++中可调用对象的概念,有以下几种定义:
1) 是一个函数指针
2) 是一个具有operator()成员函数的类对象
3) 是一个可被转换为函数指针的类对象
4) 是一个类成员(函数)指针

void fun(void){
	//...
}
struct Foo{
	void operator()(void){
		//...
	}
};
struct Bar{
	using fr_t = void (*)(void);
	static void fun(){
		//...
	}
	operator fr_t(void){
		return fun;
	}
};
struct A{
	int _a;
	void mem_func(void){
		//... 
	}
};

int main(){
	using FUN = void (*func_ptr)(void) ;  // 1.函数指针
	FUN func_ptr = fun;
	func_ptr();
	
	Foo foo;
	foo();                             // 2.仿函数

	Bar bar;      
	bar();                             // 3.可被转换为函数指针的类对象  
	
	void(A::*mem_func_ptr)(void)       // 4.类成员函数指针
		= &A::mem_func;
	int A::*mem_obj_ptr                // 类成员指针
		= &A::_a;
	A aa;
	(aa.*mem_func_ptr)();
	aa,.*mem_ob_ptr = 123;
}

上述(func_ptr,foo,bar,mem_func_ptr,mem_obj_ptr)都被称作可调用对象。上述的可调用对象并没有函数类型或者函数引用类型(只有函数指针),这是因为函数类型不能直接用来做定义对象,而函数引用更像const函数指针。

2. 可调用对象包装器std::function

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

2.1 std::function的基本用法

#include <iostream>
#include <functional>
void func(){
    std::cout <<__FUNCTION__<< std::endl;
}
class Foo{
    public:
    static int foo_func(int a){
        std::cout << __FUNCTION__ << "(" << a << ") ->:\n";
        return a;
    }
};
class Bar{
    public:
    int operator()(int a){
        std::cout << __FUNCTION__ <<"("<< a <<") ->\n";
        return a;
    }
};

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

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

    Bar bar;
    std::function<int(int)> fr3 = bar;  //绑定一个仿函数
    fr3(321);
}

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

2.2 std::function作为回调

class A{
    std::function<void()> callback;
    public:
    A(const std::function<void()>& f):callback(f){}
    void notify(){
        callback();//回调到上层
    }
};
class Foo{
    public:
    void operator()(void){
        std::cout <<__FUNCTION__<<"is executable"<< std::endl;
    }
};

int main(){
    Foo foo;
    A aa(foo);
    aa.notify();
}

从上面的例子中可以看到,std::function可以取代函数指针的作用。因为它可以保存函数延迟执行,所以适合做回调函数。

typedef void( *Func)(void);
using Func = void(*)(void);

使用函数指针类型作为成员也可以完成,但是回调函数只能是普通函数而不能是仿函数等(需要再次转型)。

2.3 std::function作为函数入参

void call_when_even(int x,const std::function<void(int)>& f){
    if(!(x & 1)){
        f(x);
    }
    else{
        std::cout <<"并未触发回调:"<<x<< std::endl;
    }
}

void output(int x){
    std::cout <<__FUNCTION__<<" parament value : "<<x<< std::endl;
}
int main(){
    int num = 10;
    while(num--)
        call_when_even(num,output);
}

从上述例子可以看出,std::function比函数指针更加灵活。

3. std::bind绑定器

std::bind用来可将于可调用对象与其参数一起绑定,绑定后的结果使用std::function进行保存,并延迟到任何我们需要的时候。
通俗讲,它有两大作用:
1)将可调用对象与其参数一起绑定为一个仿函数。
2)将多元(参数n,n>1)可调用对象转化成一元或者(n-1)元可调用对象,即只绑定部分参数。

3.1 std::bind基本用法

#include <iostream>
#include <functional>
void output(int x,int y){
    std::cout <<__FUNCTION__<<x<<","<<y<< std::endl;
}
int main(){
    std::bind(output,1,2)(); //输出:1,2
    std::bind(output,std::placeholders::_1,2)(11); //输出:11,2
    std::bind(output,2,std::placeholders::_1)(111);//输出:2,111
    //std::bind(output,std::placeholders::_1,std::placeholders::_2)(1); //error  
    std::bind(output,std::placeholders::_1,std::placeholders::_2)(100,1000);
}

从上述可得对std::bind的返回结果直接调用。可以看到,std::bind可以绑定所有参数,也可以仅仅绑定部分参数。

3.2 std::function和std::bind配合使用

class A{
    public:
    int i_ = 100;
    void output(int x,int y){
        std::cout <<__FUNCTION__<<":"<<x<<","<<y<< std::endl;
    }
};
int main(){
    A a;
    std::cout <<a.i_<< std::endl;
    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;
    std::cout <<a.i_<< std::endl;
}

通过std::function和std::bind的配合,对所有可调用对象均有了统一调用方法。

3.3 std::bind1st,std::bind2nd,std::bind

//查找元素大于10的元素个数
int count = std::count_if(cool.begin(),cool.end(),std::bind1st(std::less<int>(),10);
//等与上式子
int count = std::count_if(cool.begin(),cool.end(),std::bind2nd(std::greater<int>(),10));

//查找元素小于10的元素个数
int count = std::count_if(cool.begin(),cool.end(),std::bind1st(std::greater<int>(),10));
//等于上式子
int count = std::count_if(cool.begin(),cool.end(),std::bind2nd(std::less<int>(),10));

//查找元素大于10的元素个数
using std::placeholders::_1;
int count = std::count_if(cool.begin(),cool.end(),std::bind(std::less<int>(),10,_1));
//查找元素小于10的元素个数
int count = std::count_if(cool.begin(),cool.end(),std::bind(std::less<int>,_1,10));

std::placeholders 占位符合理使用,就不要纠结使用bind1st还是bind2nd。

3.4 使用std::bind组合函数

//判断大于5小于10的元素(左开右闭)
auto f = std::bind(std::logical_and<bool>(),
	std::bind(std::greater<int>(),std::placeholders::_1,5),
	std::bind(std::less_equal<int>(),std::placeholders::_1,10));
int count = std::count_if(cool.begin(),cool.end(),f);

3.5 使用std::bind和std::function进行黑盒测试

typedef std::function<bool(const char*,int)> REC;    //回调函数类型
Type func(int ,int,REC);

在某个类中存在函数func,需要参数int,int,REC。于是在调用时可能如下:

bool awk(cosnt char *str,int b);
func(10,10,awk);

但实际这样的函数在进行测试不能解决问题(需要重载或者修改原函数接口,而这么做不符合软件的开闭原则),因而需要使用lambda或者std::bind或者函数对象。

//std::bind
bool sawk(std::string &s,const char* str,int b);
std::string s;
auto t1 = std::bind(sawk,std::ref(s),std::placeholders::_1,std::placeholders::_2);
func(2,1,t1);

// lambda
std::string s;
auto t2 = [&s](const char*,int b)->bool {return true;};
func(2,1,t2);

//function object
class TS{
public: 
    bool operator()(const char *str,int a);
private:
    std::string d;
};
TS t3;
func(2,1,std::ref(t3));

通过传入std::bind绑定的函数,lambda,以及仿函数得到的函数符合传入REC的类型,但是可以额外附带的参数可以完成测试功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值