当前位置: 首页 > news >正文

C++11函数包装器

目录

std::function

注意事项

包装静态成员函数

包装非静态成员函数

std::bind

用法

应用场景


std::function

function是C++11引入的类,可以用任何可调用对象作为参数,构造出一个新对象。

可调用对象有函数指针,仿函数,lambda等

下面是function的声明

可以看到是一个类模板,第一个参数是函数返回值类型,第二个是可变参数模板,表示函数的参数列表

先看一个例子

#include<iostream>
//使用function需要包含头文件
#include<functional>
int main()
{
	std::function<int(int, int)> add = [](int a, int b)->int
    {
	    return a + b; 
    };
    std::cout << add(1, 2) << std::endl;
	return 0;
}

这是对lambda表达式的包装,关于lambda表达式,详见 C++11 lambda表达式-CSDN博客

function可以将一系列参数列表和返回值相同的函数用相同的类型接收,这在某些情况下提供了极大的便利。

比如对表达式求值,要根据符号是+、-、*、/中的某一个进行运算,一种选择是使用条件分支语句,也就是if或者switch case语句,但是写起来有些繁琐,这时可以使用function

#include <map>
#include <functional>
int main()
{
	std::map<char, std::function<int(int, int)>> op
	{
		{'+', [](int x, int y)->int {return x + y; }},
		{'-', [](int x, int y)->int {return x - y; }},
		{'*', [](int x, int y)->int {return x * y; }},
		{'/', [](int x, int y)->int {return x / y; }}
	};
	std::cout << op['+'](10, 20) << std::endl;
	return 0;
}

使用function就可以省去大量的条件判断,并且map可以随时扩展,增加新的功能。

注意事项

包装静态成员函数

class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return a + b;
	}
};
int main()
{
	std::function<int(int, int)> f1 = &Plus::plusi;
	return 0;
}

包装非静态成员函数

第一种方法

class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return a + b;
	}
};
int main()
{
	Plus p;
	std::function<double(Plus*, double, double)> f1 = &Plus::plusd;
	std::cout << f1(&p, 1.1, 2.2) << std::endl;
	return 0;
}

由于非静态的成员函数会有一个this指针,于是在包装时需要加上这个指针参数,在调用时要显式加上这个参数

还有一种写法

class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return a + b;
	}
};
int main()
{
	std::function<double(Plus, double, double)> f1 = &Plus::plusd;
	std::cout << f1(Plus(), 1.1, 2.2) << std::endl;
	return 0;
}

这种写法与第一种的区别在于,不传入Plus*,而是传入Plus,可能你不禁会想,为什么传入Plus也可以,这里解释一种理解方式 :

function接收函数指针实例化后是一个类,这个类有operator()方法,这个方法接收了参数,在我举的例子中,参数是Plus(), 1.1, 1.2,内部不是直接显式地把这几个参数传入回调函数,而是使用类似p.plusd(1.1,1.2)或者p->plus(1.1, 1.2)进行成员函数调用。所以这里可以选择不传指针。

而且,this指针不支持显式传递,所以底层当然不是直接显式传参。

std::bind

用法

bind是一个函数模板

// 原型如下:
#include <functional>
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2)
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

bind以某个可调用对象为参数,生成一个新的可调用对象,fn是可调用对象。

直接看下面的例子

int sub(int a, int b)
{
	return a - b;
}		
int main()
{
	auto sub1 = std::bind(sub, 1, std::placeholders::_1);
	std::cout << sub1(2) << std::endl;
	return 0;
}

sub是传入的可调用对象,1传给sub的第一个形参也就是a,std::placeholders是一个命名空间,里面有从_1,_2,...,_n很多个数字,表示第一个实参,第二个实参,...,第n个实参。

在这里表示传入的实参2

bind返回了一个可调用对象,这个可调用对象的第一个参数a被固定成了a,第二个参数是sub1传入的第一个参数,经过bind作用后,你可以理解为sub1是下面的函数

int sub1(int b)
{
	return 1 - b;
}

再看一个复杂点的例子

int sub(int a, int b, int c)
{
	return a - b - c;
}
int main()
{
	auto sub1 = std::bind(sub, 1, std::placeholders::_2, std::placeholders::_1);
	std::cout << sub1(2, 3) << std::endl;
	return 0;
}

a被定为了1,b是传入的第二个实参,c是传入的第一个实参

相当于

int sub1(int b, int c)
{
	return 1 - c - b;
}

 当然也可以

int sub(int a, int b, int c)
{
	return a - b - c;
}
int main()
{
	auto sub1 = std::bind(sub, std::placeholders::_2, 2, std::placeholders::_1);
	std::cout << sub1(2, 3) << std::endl;
	return 0;
}

这时sub1相当于

int sub1(int a, int c)
{
	return c - 2 - a;
}

理解了bind的用法后,来看看它的应用场景

应用场景

在使用function包装非静态成员函数时,需要多传一个参数,以上面function举的例子来看

class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return a + b;
	}
};
int main()
{
	std::function<double(Plus, double, double)> f1 = &Plus::plusd;
	std::cout << f1(Plus(), 1.1, 2.2) << std::endl;
	return 0;
}

需要加一个Plus(),这时就可以使用bind

class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return a + b;
	}
};
using namespace std::placeholders;
int main()
{
	std::function<double(double, double)> f1 = std::bind(&Plus::plusd, Plus(), _1, _2);
	std::cout << f1(1.1, 2.2) << std::endl;
	return 0;
}

现在使用f1就不用再传Plus()了。

而且,如果有一个函数,有绑定某个参数为某些值得需求时,bind也能派上用场

相关文章:

  • 防重复提交详解:从前端Vue到后端Java的全面解决方案
  • Matlab 风力发电机磁悬浮轴承模型pid控制
  • 在办公电脑上本地部署 70b 的 DeepSeek 模型并实现相应功能的大致步骤
  • 点灯、点各式各样的灯
  • yarn调度过程
  • C++20 指定初始化器
  • 算是解决可以访问github但无法clone的问题
  • 【Java 优选算法】分治-归并排序
  • 代码随想录|二叉树|11完全二叉树的节点个数
  • spring-ai-alibaba-examples项目编译运行
  • 代码随想录算法训练营第七天|组合、组合总和III和电话号码的字母组合
  • 基于cat1的贵重物品的状态和位置小型监控系统特色解析
  • 第十五届蓝桥杯C/C++B组拔河问题详解
  • OrioleDB: 新一代PostgreSQL存储引擎
  • stl之string的详解
  • 基于云的内容中台驱动企业智能服务升级
  • 并发编程--具名管道
  • HarmonyOS-应用程序框架基础
  • 应用于电池模块的 Fluent 共轭传热耦合
  • 【源码分析】Nacos服务注册源码分析-客户端
  • 大理杨徐邱再审上诉案宣判:驳回上诉,维持再审一审判决
  • 四川省社科联期刊:不建议在读硕士、博士将导师挂名为第一作者
  • 第二艘国产大型邮轮实现坞内起浮,重点强化邮轮供应链本土化建设
  • 中国贸促会:有近50%的外贸企业表示将减少对美业务
  • 全过程人民民主研究基地揭牌,为推动我国民主政治建设贡献上海智慧
  • 上海浦东单价超10万楼盘228套房源开盘当天售罄,4月已有三个新盘“日光”