C++11——包装器


该篇为lambda表达式的延申,请在熟知lambda表达式的基础上阅读该文章

一文详解C++11lambda表达式icon-default.png?t=N7T8https://blog.csdn.net/qq_74260823/article/details/134839319?spm=1001.2014.3001.5501


 包装器的由来

这同样是一个不属于C++原始风味的语法

我们在lambda表达式中讲到过,就算lambda表达式是相同的,其类型也不同,不能进行相互赋值等操作。而这还只是在lambda表达式之间,如果有一个仿函数和一个lambda表达式,就算其功能相同,我们想把他们联系起来,那也堪比登天一般难。

//仿函数
struct plus
{
	int operator()(int num1, int num2)
	{
		return num1 + num2;
	}
};

int main()
{
	//lambda表达式
	auto lambda_plus = [](int a, int b) {return a + b; };

	//两者功能相同,却不能相互联系
	lambda_plus = plus();
}

就好比……

这就导致在很多应用场景,lambda表达式和仿函数使用起来是非常不便的。比如之前我们大量进行回调函数时,会采用函数指针的方式,构建一个函数指针数组,调用时按其对应的下标调用即可。但是lambda表达式则不然,每一个lambda表达式类型都不同,我们没办法去开辟一个定类型的数组,也就意味着传统的函数指针的方式是不可行的。

 于是,带着这个使命,包装器便产生了


包装器

包装器的作用是将函数,仿函数,lambda表达式的类型统一,使其可以相互联系,相互转化

包装器的定义

包装器在头文件functional中,其定义长这个鬼样子

 其中,ret代表返回值的类型,而Args代表参数包的类型。

直接来看例子吧,以加法的函数为例

也就是说,在function的模板里,第一个参数是返回值的类型,随后在括号里依次传入函数参数列表的类型,一一对应

而后赋值的时候,只需要让其等于已经定义过的可调用对象,便完成了赋值,可以正常进行使用。

包装器的类型

还是刚刚的例子,我们分别包装函数,函数指针,仿函数,lambda表达式,看看其类型的情况 

//函数
int normal_plus(int num1,int num2)
{
	return num1 + num2;
}

//仿函数
struct functor_plus
{
	int operator()(int num1, int num2)
	{
		return num1 + num2;
	}
};

int main()
{
	//lambda表达式
	auto lambda_plus = [](int a, int b) {return a + b; };

	//使用包装器包装函数
	function<int(int, int)> function1 = normal_plus;

	//使用包装器包装函数指针
	function<int(int, int)> function2 = &normal_plus;

	//使用包装器包装仿函数
	function<int(int, int)> function3 = functor_plus();

	//使用包装器包装lambda表达式
	function<int(int, int)> function4 = lambda_plus;

	//其类型都为class std::function<int __cdecl(int,int)>
	cout << typeid(function1).name() << endl;
	cout << typeid(function2).name() << endl;
	cout << typeid(function3).name() << endl;
	cout << typeid(function4).name() << endl;

	//也可以进行相互转化
	function1 = function2;
}

最终,无论最开始他们是什么类型,到了最后都变成了类似于function<int(int,int)>的类型,也就可以进行相同类型之间的任何操作

包装器的应用场景 

通过包装器,我们可以轻轻松松实现函数回调

int main()
{
    //实现一个计算器
	map<string, function<int(int, int)>> calculator =
	{
		{"加法",[](int a,int b) {return a + b; }},
		{"减法",[](int a,int b) {return a - b; }},
		{"乘法",[](int a,int b) {return a * b; }},
		{"除法",[](int a,int b) {return a / b; }}
	};

	calculator["加法"](1, 20);
}

如果没有包装器,那么只能采用普通函数构建函数指针数组,既会产生大量命名冲突的风险,又会导致程序的简洁性大大降低。但是采用包装器,便把lambda表达式简洁易读的特点放到了最大。

包装器存在的问题

如果我们需要包装一个类中函数,我们应该怎么办?

 

class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return a + b;
	}
};

int main()
{
	//静态成员函数——没有this指针
	std::function<int(int, int)> funci = &Plus::plusi;
    //可以不写&
	cout << funci(1, 2) << endl;
	
	//非静态——有this指针
	std::function<double(Plus, double, double)> funcd = &Plus::plusd;
    //包装的时候必须有一个this的位置(必须写&)
	
    cout << funcd(Plus(), 1.1, 2.2) << endl;//通过匿名对象调用成员函数
	
	return 0;
}

如果是类中的静态函数,则和调用普通函数相同;而如果是类中非静态函数,则必须要在参数列表中加上this指针的类型,并且赋值时只能赋值为函数指针,调用时也必须传入this指针

 但是,往往我们使用的时候,并不喜欢每次都写this指针,那有没有办法省略掉this指针呢?

当然,这便是新的特性:bind


bind

std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺序调整等操作。 

bind的定义

继续看例子吧

也就是说,bind实际上是拷贝了其他函数或包装器,来定义一个新的包装器,在bind的第一个参数中,我们输入被绑定的名称,在后面依次输入参数的顺序,一直到参数的个数与被绑定的函数的参数个数相同。

这个参数个数我们可以进行一些小调整

强制参数

int normal_plus(int num1, int num2)
{
	return num1 + num2;
}

int main()
{
	//第一个参数强制传入10
	//包装器模板参数中只需要传入一个参数
	//而参数的顺序为剩下参数的顺序
	function<int(int)> fplus = bind(normal_plus, 10, placeholders::_1);

	//调用时只需要传入一个参数
	fplus(2);//10+2
}

交换参数的顺序

int normal_plus(int num1, int num2)
{
	return num1 + num2;
}

int main()
{
	//交换参数的顺序
	function<int(int, int)> fplus = bind(normal_plus, 
										placeholders::_2,//第一个参数传入num2
										placeholders::_1//第二个参数传入num1
										);

	//传入的参数顺序为绑定的顺序
	fplus(10, 3);//3+10;
}

而通过这种方法,我们便可以写死this指针,不需要每次都传入this了


  • 25
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值