std::function and bind

可调用对象(Callable Object)

  1. 函数指针

  2. 具有operator()成员函数的类对象(仿函数)

  3. 可被转换为函数指针的类对象

  4. 类成员函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
void func(void) 
{
std::cout<<"函数指针"<<std::endl;
}

struct Foo{
void operator()(void){
std::cout<<"仿函数"<<std::endl;
}
};

struct Bar{
using fr_t = void(*)(void);

static void func(void){
//...
std::cout<<"指向函数的类对象"<<std::endl;
}

operator fr_t(){
return func;
}
};

struct A{
int a;
void mem_func(void){
//...
std::cout<<"成员函数"<<std::endl;
}
};

int main(){
void (*func_ptr)(void) = func;
func_ptr();

Foo foo;
foo();

Bar bar;
bar();

void (A::*mem_func_ptr)(void) = A::mem_func;
int A::*mem_obj_ptr = &A::a;

A aa;

(aa.*mem_func_ptr)();

aa.*mem_obj_ptr = 123;
std::cout<<aa.a<<std::endl;
}

可调用对象的包装器-std::function

简介

它是一个类模板,可以容纳类成员(函数)指针(需要用bind该函数将实际对象绑定)之外的所有可调用对象。通过指定他的模板参数,它可以统一的方式处理函数、函数对象、函数指针,并允许保存和延迟执行它们

弹性更强

  • 对于函数对象的返回值可以进行适当隐式转换(short and float -> int)(兼容性)
  • 对于函数对象的参数可以被隐式转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <iostream>
#include <functional>

void func(){
std::cout<<__FUNCTION__<<std::endl;
}

class Foo{
public:
using f_int = int(*)(int);
static int foo_func(int x){
std::cout<<__FUNCTION__<<std::endl;
return x;
}
operator f_int () { return foo_func;}
};

class Bar{
public:
int operator()(int x){
std::cout<<__FUNCTION__<<std::endl;
return x;
}
};

struct A{
int a;
void mem_func(void){
//...
std::cout<<__FUNCTION__<<std::endl;
}
};
//function入参
void call_when_even(int x,const std::function<void(int)>& f)
{
if(!(x&1))
{
f(x);
}
}

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

void test_f(){
std::function<void(void)> fr1 = func;
fr1();

std::function<int(int)> fr2 = Foo::foo_func;
std::cout<<fr2(2)<<std::endl;
//隐式转换类型
std::function<int(int)> fr3 = Foo();
std::cout<<fr3(3)<<std::endl;

std::function<int(int)> fr4 = Bar();
std::cout<<fr4(4)<<std::endl;

//bind和function联合
std::cout<<"----bind and function---"<<std::endl;
A a;
auto bf1 = std::bind(&A::mem_func,&a);//第二个引用可有可无
auto bp1 = std::bind(&A::a,a);//第二个取引用只是指向该类的对象
bp1() = 123;
std::cout<<bp1()<<std::endl;
std::cout<<a.a<<std::endl;
std::function<void(void)> fr5 = bf1;
fr5();
}

int main(){
test_f();
for(int i = 0;i<10;++i){
call_when_even(i,output);
}
std::cout<<std::endl;
return 0;
}

std::bind绑定器

std::bind用来将可调用对象与其参数一起进行绑定,绑定后的结构可以使用std::function进行保存,并延迟调用到任何我们需要的时候

作用

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

  2. 将多元参数可调用对象转成一元或者(n-1)元金额调用对象,及只绑定部分参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <functional>
class FuncEntity{
public:
int eFunc(int a){
return a;
}
};

void test_b(){
FuncEntity e;
auto a = std::bind(&FuncEntity::eFunc,e,std::placeholders::_1);

std::cout<<a(2);
}


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

void output(int x)
{
std::cout<<x<<" ";
}
void output_add_2(int x)
{
std::cout<<x+2<<' ';
}

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

}

占位符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>        // std::cout
#include <functional> // std::bind
void output(int x, int y)
{
std::cout << x << " " << y << std::endl;
}
int main(void)
{
std::bind(output, 1, 2)(); // 输出: 1 2
std::bind(output, std::placeholders::_1, 2)(1); // 输出: 1 2
std::bind(output, 2, std::placeholders::_1)(1); // 输出: 2 1
// error: 调用时没有第二个参数
std::bind(output, 2, std::placeholders::_2)(1);
std::bind(output, 2, std::placeholders::_2)(1, 2); // 输出: 2 2
// 调用时的第一个参数被吞掉了
std::bind(output, std::placeholders::_1,std::placeholders::_2)(1, 2); // 输出: 1 2
std::bind(output, std::placeholders::_2,std::placeholders::_1)(1, 2); // 输出: 2 1
return 0;
}

bind简化和增强bind1st和bind2nd

其实bind简化和增强了之前标准库中bind1st和bind2nd,它完全可以替代bind1s和bind2st,并且能组合函数。我们知道,bind1st和bind2nd的作用是将一个二元算子转换成一个一元算子,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
// 查找元素值大于

10的元素的个数

int count = std::count_if(coll.begin(), coll.end(),
std::bind1st(less<int>(), 10));
// 查找元素之小于

10的元素

int count = std::count_if(coll.begin(), coll.end(),
std::bind2nd(less<int>(), 10));

本质上是对一个二元函数less的调用,但是它却要分别用bind1st和bind2nd,并且还要想想到底是用bind1st还是bind2nd,用起来十分不便。

现在我们有了bind,就可以以统一的方式去实现了,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
// 查找元素值大于

10的元素的个数

int count = std::count_if(coll.begin(), coll.end(),
std::bind1st(less<int>(), 10));
// 查找元素之小于

10的元素

int count = std::count_if(coll.begin(), coll.end(),
std::bind2nd(less<int>(), 10));

这样就不用关心到底是用bind1st还是bind2nd,只需要使用bind即可。

2.使用组合bind函数

bind还有一个强大之处就是可以组合多个函数。假设要找出集合中大于5小于10的元素个数应该怎么做呢?

首先,需要一个用来判断是否大于5的功能闭包,代码如下:

1
std::bind(std::greater<int>(), std::placeholders::_1, 5);

这里std::bind返回的仿函数只有一个int参数。当输入了这个int参数后,输入的int值将直接和5进行大小比较,并在大于5时返回true。

然后,我们需要一个判断是否小于10的功能闭包:

1
std::bind(std::greater<int>(), std::placeholders::_1, 5);

有了这两个闭包之后,只需要用逻辑与把它们连起来:

1
2
3
4
using std::placeholders::_1;
std::bind(std::logical_and<bool>(),
std::bind(std::greater<int>(), _1, 5),
std::bind(std::less_equal<int>(), _1, 10));

然后就可以复合多个函数(或者说闭包)的功能:

1
2
3
4
5
6
7
8
9
10
11
using std::placeholders::_1;
// 查找集合中大于

5小于

10的元素个数

auto f = std::bind(std::logical_and<bool>(),
std::bind(std::greater<int>(), _1, 5),
std::bind(std::less_equal<int>(), _1, 10));
int count = std::count_if(coll.begin(), coll.end(), f);