C++11 function包装器

前言

在C++中,有三种可调用对象:函数指针,仿函数,lambda表达式。
三者有相似的作用和效果,但使用形式有很大的差异。
为了进行统一,C++11引进了function包装器

在这里插入图片描述

一. function的使用

首先,要想使用function,需要包含functional这个头文件

在这里插入图片描述

function包装器本质是一个类模板

std::function在头文件<functional>
// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;

模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参

以往,如果要实现一个加法,函数指针,仿函数,lambda表达式的实现方式如下:

#include<iostream>
#include<functional>

using namespace std;

//函数
int func(int a, int b)
{
	cout << "int func(int a, int b)" << endl;
	return a + b;
}
//仿函数
class Func
{
public:
	int operator()(int a, int b)
	{
		cout << "int operator()(int a, int b)" << endl;
		return a + b;
	}
};


int main()
{
	//函数指针
	int(*fp)(int, int) = func;
	fp(1, 3);
	
	//仿函数
	Func ff;
	ff(1, 3);
	
	//lambda表达式
	auto fl=[](int a,int b)->int
	{
		cout << "[](int a,int b)->int{}" << endl;
		return a + b;
	};
	fl(1, 3);
	
	return 0;
}

虽然都可以完成需求,但使用方式差别太大,而function就可以将三者统一包装起来

在这里插入图片描述

可以看到,function包装器统一封装了三种可调用对象,且最终类型相同,这就是包装器的使用
< >中,第一个int是返回值类型,( )中是参数类型

functiona的使用还有如下场景:

map<string, function<int(int, int)>>funcMap;
funcMap["函数指针"] = fp;
funcMap["仿函数"] = Func();
funcMap["lambda"] = [](int a, int b)->int
{
	cout << "[](int a,int b)->int{}" << endl;
	return a + b;
};

二. function对成员函数的包装

除了以上的三种可调用对象,成员函数同样可以使用function包装,不过使用有些不同

首先,对于静态成员函数,function的包装没有什么差别

class Func
{
public:
	//静态成员函数
	int static staFunc(int a)
	{
		cout << "int static staFunc(int a)" << endl;
		return a;
	}
	//普通成员函数
	int norFunc(int a)
	{
		cout << "int norFunc(int a)" << endl;
		return a;
	}
};

int main()
{
	//静态成员函数
	//function<int(int)>f1 = Func::staFunc;//可以不需要&
	function<int(int)>f1 = &Func::staFunc;//需要指类域
	
	return 0;
}

对于成员函数,包装器要求&,不过静态成员函数可以不使用&
但是对于普通成员函数,即使加了&,也不行

在这里插入图片描述

因为普通的成员函数其实有一个隐藏的参数——this指针
所以如果要包装普通成员函数,function的类模板还需要加上类对象

//普通成员函数
function<int(Func,int)>f2 = &Func::norFunc;
f2(Func(),10);//使用

PS:类对象不是对应this指针,而是function调用成员函数时,需要通过类对象调用成员函数
其实也可以传Func*,function就是通过类对象指针调用成员函数
但是这样就无法使用匿名对象,因为匿名对象是右值,不能取地址

function<int(Func*,int)>f2 = &Func::norFunc;
f2(&Func(),10);//错误

三. bind绑定

functional头文件中,还有一个比较实用的函数适配器——bind

bind是一个函数模板,接收一个可调用对象,生成一个新的可调用对象来适应原对象的参数列表
一般而言,我们可以使用bind把一个原本需要传N个参数的函数fn,通过绑定一些参数,返回一个需要传M个(M可以大于N,但这么做没什么意义)参数的新函数。
同时,使用bind函数还可以实现参数顺序的调整等操作

在这里插入图片描述

1. 调整参数顺序

通过bind绑定可以调整参数顺序
比如:

void Func(int a, int b)
{
	cout << a << " ";
	cout << b << endl;
}

如果我们要调整参数a和b的顺序

bind的使用如下:

int main()
{
	function<void(int, int)>f1 = Func;
	f1(10, 20);

	auto f2 = bind(f1, placeholders::_2, placeholders::_1);
	f2(10, 20);

	return 0;
}

placeholders是头文件functional中的一个命名空间

在这里插入图片描述

placeholders::_1和placeholders::_2都是占位符
function本质是仿函数,而bind是根据占位符,和提供的可调用对象重新生成仿函数。
因为bind是函数模板,参数是万能引用,所以函数指针,仿函数,lambda同样可以调整

auto f3 = bind(Func, placeholders::_2, placeholders::_1);

返回的是一个_Binder类
在这里插入图片描述
内部包装了可调用对象,并且根据提供的占位符调整参数顺序

2. 调整参数个数

在使用function包装成员函数时,因为有this指针的存在,使得代码编写较为复杂,而bind则可以提前绑死参数

使用如下:

class Func
{
public:
	int norFunc(int a)
	{
		cout << "int norFunc(int a)" << endl;
		return a;
	}
};

int main()
{
	function<int(Func,int)>f1 = &Func::norFunc;
	f1(Func(), 5);
	//使用bind提前绑定Func对象
	auto f2 = bind(&Func::norFunc, Func(), placeholders::_1);
	f2(5);
}

提前绑死Func对象,就不需要再调用时,显式传Func对象

结束语

感谢你的阅读

如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值