前言
在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对象
结束语
感谢你的阅读文章来源:https://www.toymoban.com/news/detail-549243.html
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
文章来源地址https://www.toymoban.com/news/detail-549243.html
到了这里,关于C++11 function包装器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!