仿函数及可调用对象类型

一、仿函数概念及优点

仿函数是一个重载了调用运算符的,模仿函数的类。它的优点在于:

  • 灵活。有存储状态;
  • 高效。编译器内联优化;

仿函数是一个函数对象,它可以用作泛型算法的参数。由一个实例来看它是如何工作的:

// 这是一个仿函数
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<int> in{
    
    1,2,3,4,5,6,7,8,9,10}; // 假设这个容器包含一系列的值
std::vector<int> out(in.size());
// 传递一个仿函数给std::transform,这个算法对输入序列上所有的元素都调用仿函数
// 然后结果存储在输出序列
std::transform(in.begin(), in.end(), out.begin(), add_x(1)); 

std::transform算法功能是:让指定范围容器各元素作为参数传入函数对象,函数对象对参数的执行结果将会按输出至指定的容器。最后一点,仿函数能提高效率(相对于普通函数对象),编译器将会通过内联的方式完成优化。

二、预定义的仿函数

标准库#include <functional>定义了一组表示算术、关系和逻辑运算符的类,每个类分别定义了一个执行命名操作调用运算符。具体的情况可以查看:https://en.cppreference.com/w/cpp/header/functional。运算符比如说+ - >,关系== !=,逻辑&& ||等,如果我们需要对一个容器进行排序,默认情况下,使用标准的std::sort,标准算法库容器类型定义自己的排序逻辑,如从小到大(升序):

std::vector<double> 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按顺序接受两个参数,并且给出先ab排序是否正确的。如果正确,返回true,反之返回false。标准库提前定义好了这些经常用到的判断逻辑,如上述的cmp函数名就可以使用std::greater()替代。下次算法需要传入谓词的时候不妨看看仿函数有无实现。

三、function标准库统一了可调用类型

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<string,int(*)(int,int)> binops;
binops.insert("+",add);//ok
binops.insert("%",mod);//not ok

上述的map在定义的时候就给定了其值的类型int(*)(int,int)mod是一个表达式而不是一个函数指针,因此不能添加到函数表中。这时候就出现了一统天下的function模板,函数模板#include <functional>,具体的操作,请查阅https://en.cppreference.com/w/cpp/utility/functional/function,可调用对象通过这个类包裹,就能放一块了:

扫描二维码关注公众号,回复: 13038635 查看本文章
map<string,function<int(int,int)>> binops={
    
    
	{
    
    "+",add},
	{
    
    "-",std::minus<int>},
	{
    
    "*",[](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

猜你喜欢

转载自blog.csdn.net/weixin_39258979/article/details/113276589
今日推荐