仿函数是一个重载了调用运算符的类,模仿函数的类。它的优点在于:
- 灵活。有存储状态;
- 高效。编译器内联优化;
仿函数是一个函数对象,它可以用作泛型算法的参数。由一个实例来看它是如何工作的:
// 这是一个仿函数
struct add_x {
add_x(int val) : x(val) {} // 构造函数,构造仿函数加数
int operator()(int y) const { return x + y; }
private:
int x;
};
// 你可以这么使用它:
add_x add42(42); // 创建一个仿函数类实例,
int i = add42(8); // 然后调用它,告诉仿函数谁加42 这样一来,就完成了一次仿函数的调用
仿函数的构造函数让函数具有状态int x
,从operator(int y)
的结果来看,类名表示的是“你要传入的参数做什么”,显然这个简单的例子是让传入的参数加上42。除此,仿函数还可以作为算法的参数:
std::vector in{1,2,3,4,5,6,7,8,9,10}; // 假设这个容器包含一系列的值
std::vector out(in.size());
// 传递一个仿函数给std::transform,这个算法对输入序列上所有的元素都调用仿函数
// 然后结果存储在输出序列
std::transform(in.begin(), in.end(), out.begin(), add_x(1));
std::transform
算法功能是:让指定范围容器各元素作为参数传入函数对象,函数对象对参数的执行结果将会按输出至指定的容器。最后一点,仿函数能提高效率(相对于普通函数对象),编译器将会通过内联的方式完成优化。
标准库#include
定义了一组表示算术、关系和逻辑运算符的类,每个类分别定义了一个执行命名操作调用运算符。具体的情况可以查看:https://en.cppreference.com/w/cpp/header/functional。运算符比如说+
-
>
,关系==
!=
,逻辑&&
||
等,如果我们需要对一个容器进行排序,默认情况下,使用标准的std::sort
,标准算法库容器类型定义自己的排序逻辑,如从小到大(升序):
std::vector dvec{11,3,4,5,13,2};
sort(dvec.begin(),dvec.end())
结果是:2 3 4 5 11 13,那么如何降序?泛型算法同时还接受一个函数对象用于改变其排序行为:
bool cmp(double a, double b)
{
return a > b;
}
sort(dvec.begin(), dvec.end(),cmp);
cmp
是函数对象,用于定义如何排序。cmp
按顺序接受两个参数,并且给出先a
再b
排序是否正确的。如果正确,返回true
,反之返回false
。标准库提前定义好了这些经常用到的判断逻辑,如上述的cmp
函数名就可以使用std::greater()
替代。下次算法需要传入谓词的时候不妨看看仿函数有无实现。
C++有以下可调用对象类型
- 函数
- 函数指针
- lambda表达式
- bind创建的对象
- 仿函数
假设我们有以下可调用类型,虽然他们实现的功能不同,但是从函数指针的角度他们属于同一类型,都是int(int int)
。
int add(int i,int j){return i+j;}
auto mod=[](int i,int j){return i+j;}
struct divide{
int operator()(int denominator,int divisor){
return denominator/divisor;
}
}
为了方便调用,我们需要们可能需要将其放在一个函数容器里,专业点叫做函数表(function table),最简单的就是使用map
。
map binops;
binops.insert("+",add);//ok
binops.insert("%",mod);//not ok
上述的map
在定义的时候就给定了其值的类型int(*)(int,int)
,mod
是一个表达式而不是一个函数指针,因此不能添加到函数表中。这时候就出现了一统天下的function
模板,函数模板#include
,具体的操作,请查阅https://en.cppreference.com/w/cpp/utility/functional/function,可调用对象通过这个类包裹,就能放一块了:
map binops={
{"+",add},
{"-",std::minus},
{"*",[](int i,int j){return i*j;},
{"%",mod}
};
binops["+"](10,5);//10-5
binops["-"](4,5);//4-5
lambda表达式
之前的写过的一篇文章
[1] https://stackoverflow.com/questions/52327464/callback-vs-lambda-in-java [2] https://stackoverflow.com/questions/356950/what-are-c-functors-and-their-uses