浅析std::function & std::bind

前情提要

众所周知,函数(Function)封装了一系列可复用的操作集合,是各类编程语言中非常重要的概念

C++中,除了普通函数,还增加了仿函数(Functor),又称为函数对象,实际上是重载了函数调用运算符operator()的类

C++11中新增的lambda表达式其实就是匿名Functor

这些统称为可调用对象(callable entity):

  • 普通函数:
int add(int a, int b){return a + b;} 
  • lambda表达式:
auto mod = [](int a, int b){ return a % b;}
  • 函数对象类:
struct divide{
    int operator()(int denominator, int divisor){
        return denominator/divisor;
    }
};

問題点

以上三种可调用对象虽然调用形式都是 int(int,int)

但是定义形式五花八门,导致很难用统一的方式传递或保存

如果再遇到类成员函数,如何声明和调用,更是令人头大

大一统:平权衡,正度量,调轻重

天下分久必合,合久必分

是时候来统一度量衡了

std::function

std::function是一个可调用对象包装器,是一个类模板,可以容纳所有可调用对象,用统一的方式处理函数和仿函数,并允许保存和延迟它们的执行

例如:

std::function<int(int, int)> a = add;//普通函数
std::function<int(int, int)> b = mod;//lambda
std::function<int(int, int)> c = divide();//divide()创建匿名对象
a(1,2);
b(1,2);
c(1,2);

这样便可以舒适地定义、存储与调用一切可调用对象

如果这还不酷,什么是酷?—— 《Effective C++中文版(第三版)》p175

等一下

ちょっと待って,我刚刚是不是说了“一切”

我是不是忘了什么

什么呢

成员函数

成员函数的传递与调用一直是一个令人头疼的问题

但只要抓住其与普通函数的区别便可以一击毙命

成员函数 vs 普通函数

成员函数属于类,且可以使用成员变量(非static函数)

而成员变量依赖于具体实例

在不明确具体实例的情况下,成员函数没有办法正确执行

正是因为如此,成员函数其实是有一个隐含参数的(指向具体实例指针)

struct Object{
    int sum(int a,int b){return a+b;}
}
//Object::sum的形式其实是
int sum(Object* ptr,int a,int b);//隐式参数Object* ptr
//调用:
(ptr->*sum)(1,1);//->*为成员指针访问运算符

所以在调用成员函数时,必须想办法提供具体实例的指针

How

普通调用时,我们可以采用 .* 或者 ->* 运算符

但是在std::function对象中,更为简便

直接具象为第一个参数

struct Object{
    int sum(int a,int b){return a+b;}
}
Object a;
std::function<int(Object*,int,int)> func = &Object::sum;
func(&a,1,1);//直接作为第一个参数即可

如果你不想在调用时绑定实例,也可以通过std::bind函数在定义时绑定

Object a;
std::function<int(int,int)> func = std::bind(&Object::sum, &a);
func(1,1);

这样就把对象a的地址绑定到了函数对象func的第一个参数上(从三个参数变成了两个参数)

顺便一提,std::bind其实是将对象进行了拷贝

也就是说,如果对象a销毁,func照样可以正常调用

如果想将拷贝改为引用,可以采用ref(a)形式

lambda

其实,调用std::bind进行绑定还是有些麻烦的

毕竟,只有"懒",才能推动自动化

lambda其实本身就是为了简化函数定义而存在的

而且其本质是重载了operator()的匿名函数对象类

所以在传递给std::function时,是一个对象,也就无需额外传递实例信息

std::function<int(int, int)> add = [](int a, int b) { return a + b; };
add(1,2);

如果需要其余变量或者所在类的成员变量,可以通过lambda的捕获列表传递

peace

Ref

C++11中的Lambda表达式构成之——捕获值列表

bind原理图释 - xusd-null - 博客园

关于std::bind绑定栈变量对象的思考

C++11 中的std::function和std::bind - 简书

如何使用std::function指向类的成员函数? - 知乎

猜你喜欢

转载自juejin.im/post/7114317985698807815