本文的内容在头文件#include <functional>
using namespace std;
using namespace std::tr1;
using namespace std::tr1::placeholders;
函数对象
函数对象是指重载了括号操作符operator()的类对象。因为可以像函数一样调用,所以称之为函数对象,其优点是既可以像函数一样调用,又可以像类一样让每个对象都拥有各自的成员变量值,而且可以作为模板参数,定义不同类型的容器。
例如:
#include <functional>
using namespace std;
//定义一个类,重载operator()
class test
{
public:
bool operator() (int i) const
{
return i < 3;
}
};
//调用
{
test funcObj;
funcObj(5); //像函数调用一样使用test类的operator()
}
需要说明的是,在《C++ STL标准库-第2版》中提到,对于返回bool的判断型operator(),为了保证在传入参数不变的情况下,即使多次被调用,其返回值始终不变,所以需要声明为const.
下面列举预定义的函数对象:
Lambda表达式
Lambda表达式指的是函数定义与调用写在一起的语法,其具体的使用方法本文不做介绍,其好处在于可以共享上下文的数据,而且使用起来便捷直观。一个Lambda表达式其代表的实际上就是一个函数对象
int t = 10;
auto func = [=](int i)->int {
return i + t;
};
int out = func(2); //返回12
函数适配器binder
bind()返回的是一个函数对象,其operator()会对bind()第一个形参的函数调用进行包装(可多层嵌套),例如:
-
bind包装:函数对象的operator()
bind()的第一个参数传入函数对象,后面是调用的参数
#include <functional>
using namespace std;
using namespace std::tr1;
using namespace std::tr1::placeholders;
//定义一个类,重载operator()
class test
{
public:
int operator() (int x, int y, int z)
{
return x+y+z;
}
};
{
test funcObj; //定义函数对象
auto bindFunc = bind(funcObj, std::tr1::placeholders::_2, 10, std::tr1::placeholders::_1);
int res = bindFunc(100, 200); //返回310,即200+10+100
}
上面代码中的bindFunc就相当于一个具有以下operator()的函数对象:
int operator() (int para1, int para2)
{
return funcObj(para2, 10,para1); //直接返回对funcObj的operator()的调用结果
}
所以,当调用bindFunc(100, 200);时,实际上就是执行:funcObj(200,10,100);,即200+10+100
-
bind包装:类成员函数
bind()的第一个参数传入成员函数,第二个参数传入的是类对象指针,后面是调用的参数
1.用法一
class test
{
public:
int fun1(int x, int y)
{
return i + y;
}
};
{
test obj;
auto bindFunc = bind(&test::fun1, &obj, std::tr1::placeholders::_1, 1314);
int res = bindFunc(520);
}
此时代码中的bindFunc就相当于一个具有以下operator()的函数对象:
int operator() (int para1)
{
return obj->fun1(para1, 1314);
}
所以,当调用bindFunc(520);时,实际上就是执行:obj->fun1(520, 1314);,即520+1314
2.用法二
std::tr1::placeholders::_1、std::tr1::placeholders::_2称作占位符,我们利用占位符,还有这样玩,例如:
auto bindFunc = bind(&test::fun1, std::tr1::placeholders::_1, 666, 888);
int res = bindFunc(test());
此时代码中的bindFunc就相当于一个具有以下operator()的函数对象:
int operator() (const test& t)
{
return t->fun1(666, 888);
}
所以,当调用bindFunc(test());时,实际上就是执行:test()的fun1(666, 888);,即666+888
这种玩法在《C++ STL标准库-第2版》中有也有提到,书上介绍了如何利用这种机制调用STL的算法函数,此处附上书上的部分内容:
bind包装:嵌套调用(可搭配STL算法函数)
bind包装:类的数据成员
函数类模板function<T>
类模板function<T>是一个类模板,但我们可以将它简单理解为升级版的更加强大的函数指针。
它不但可以指向普通函数,还可以指向函数对象,正如我们上文说的,Lambda表达式、bind()返回的也是函数对象,所以只要利用bind()就能使它指向类成员函数,综上所诉,function<T>可以指向普通函数、Lambda表达式、bind()创建的对象、类成员函数,比函数指针强大多了。
function<T>的简单使用
<T>表示的是函数的调用方式,例如function<int (double, string)>,int表示的是返回值类型,(double, string)表示的是形参列表,第一个参数类型是double,第二个参数类型是string。
下面例子是function<T>指向普通函数的使用方法:
//定义一个普通函数
int testFunc(int x, string s)
{
return x;
}
/*定义一个调用时形参列表为(int, string)的function<T>,指向test*/
function<int (int, string)> myfunc1 = testFunc;
auto res = myfunc1(5, "good"); //返回5
/*定义一个调用时形参列表为(double, string)的function<T>,指向test*/
function<int (double, string)> myfunc2 = testFunc;
//这里实际调用的是testFunc(int x, string s)
//相当于调用了testFunc(1.1, "good"),其中,double类型的1.1会被强制转换为int类型的1
auto res = myfunc2(1.1, "good"); //返回1
function<T>与重载的函数
这里直接引用《Primer c++ -第5版》书上的内容(书上写得非常好,偷个懒~)
function<T>指向函数对象、Lambda、bind()生成的对象、类成员函数
function<int (int)> myfunc;
test obj; //定义一个函数对象obj
/***指向函数对象***/
myfunc = obj;
myfunc(520); //实际调用的是对象obj的int operator()(int)
/***指向Lambda表达式***/
myfunc = [](int x) -> int {
retrun x;
};
myfunc(1314); //实际调用的是Lambda表达式的内容
/***指向bind()生成的函数对象***/
myfunc = std::tr1::bind(obj, std::tr1::placeholders::_1);
myfunc(886); //实际调用的是对象obj的int operator()(int)
/***指向类成员函数(这里使用bind做了包装,可能不是很直观,如果看不懂可以回看bind部分的介绍)***/
myfunc = bind(&test::fun1, &obj, std::tr1::placeholders::_1, 1314);
myfunc(886); //实际调用的是对象obj的成员函数int fun1(int, int),即fun1(886, 1314),返回int
哇噢~可算写完了~~
如有错误,欢迎评论指出,感谢~~