函数对象
重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象。又称仿函数。以下内容主要包括`lambda和function’(函数包装器)。
仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。
lambda表达式
Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。
匿名函数
所谓匿名函数,其实类似于python中的lambda函数,其实就是没有名字的函数
。使用匿名函数,可以免去函数的声明和定义
。这样匿名函数仅在调用函数
的时候才会创建函数对象
,而调用结束后立即释放
,所以匿名函数比非匿名函数更节省空间
。
必包
闭包就是能够读取其他函数内部变量的函数。例如在C++中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接
起来的桥梁。
lambda表达式的几种写法
1.值捕获
值捕获,使用[=]
让编译器自动推倒捕获列表,lambda表达式定义前的函数变量都可以捕获。[variable_name]
只捕获具体的变量。
1 | void test_lambada(){ |
引用捕获
引用捕获,使用[&]
开启引用捕获,捕获范围与值捕获一致。[variable_name]
只捕获具体的变量,此时传入的变量值是可以修改的。
1 | /*[&]开启引用捕获,&后面无参数表示全局引用捕获 |
泛型lambda
使用auto类型来完成lambda初始化,[]
不能有auto,其与模版产生有冲突,无法推倒出正确的模板类型。正确的写法是将auto放入parma列表中
1 | void Lambda_2(){ |
万能引用配合lambda
使用auto&&配合lambda实现lambda中的函数调用
1 | void Lambda_4(){ |
函数对象包装器
在 C++11 中统一了这些概念,将能够被调用的对象的类型,统一称之为可调用类型,而这种类型,便是通过std::function 引入的。
C++11 std::function 是一种通用、多态的函数封装,可以对任何可调用目标实体进行存储、复制和调用操作。
它也是对 C++中现有的可调用实体的一种类型安全的包裹(相对来说,函数指针的调用不是类型安全的),换句话说,就是函数的容器。
当我们有了函数的容器之后便能够更加方便的将函数、函数指针作为对象进行处理。
普通函数封装成函数对象
1 | //functional<return_type(parma_type_1,parma_type_2)> name = function_name;//普通函数封装 |
匿名函数对象封装成函数对象
1 | //functional<return_type()> name = lambda();//匿名函数 |
类成员函数和重载函数封装成函数对象
1 | //class_member:funtional<return_type(class *,parma_type) name = class::member_name; |
Std::bind/std::placeholder
std::bind 是用来绑定函数调用参数的,它解决的需求是:
我们有时候可能并不一定能够一次性获得调用某个函数的全部参数。
通过这个函数,我们可以将部分调用参数提前绑定到函数身上成为一个新的对象,然后在参数齐全后,完成调用。
好处是降低耦合封装接口,屏蔽底层,使用延时调用。
1 | int foo(int a, int b, int c) { |
函数对象作为参数使用
1 | void test_1(function<int(int,int)> tmp){ |