函数对象
传递给算法的函数型参数并不一定是函数,也可能是类似函数的对象。这种对象称为函数对象,或者称为仿函数。
定义一个函数对象
任何东西,只要其行为像函数,那么它就是一个函数。因此你定义了一个对象,行为像函数,它就可以被当作函数使用。也就说它具备着函数行为。什么叫函数行为:使用小括号传递参数,借以调用某个东西。
function(arg1, arg2);
如果希望一个对象也能这样使用,那么就必须让它也有可能被调用,通过小括号的运用和实参的传递。那么就需要定义operator(),并给予合适的参数类型。
class X
{
public:
//define "function call" operator
return-value operator() (arguments) const;
};
那么,你现在可以把X的对象当作函数来使用了
X fo;
fo(arg1, arg2);
//等价与
fo.operator()(arg1, arg2);
下面是一个完整的例子
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
class PrintInt
{
public:
void operator()(int elem) const
{
cout << elem << " ";
}
};
int main()
{
vector<int> coll{1,2,3,4,5,6};
for_each(coll.begin(), coll.end(), PrintInt());
}
注意:for_each的最后一个参数PrintInt()的作用是创建一个临时对象作为for_each的一个参数
//for_each的算法大致如下
namespace std
{
template <typename Iterator, typename Operation>
Operation for_each(Iterator act, Iterator end, Operator op)
{
while (act != end)
{
op(*act);
++act;
}
return op;
}
}
函数对象的优势:
- 函数对象是一种带状态的函数(可以拥有成员函数和成员变量,承载信息更多)
- 一个template的类,根据不同的类型,可以生成不同的类型,不同的对象,代码量比函数少
- 速度快,很多细节在编译期就确定了,可能得到更好的优化
比如,对于第一个优势
class AddValue
{
public:
AddValue(int v) : _value(v){}
void operator()(int &elem) const
{
elem += _value;
}
private:
int _value;
};
list<int> coll{1,2,3,4,5,6,7};
for_each(coll.begin(), coll.end(), AddValue(10));
STL标准库里面有很多自定义的函数对象
#include <functional>//自定义函数对象的头文件
set<int, less<int> > coll;
set<int, greater<int>> coll;
transform(coll.begin(), coll.end(), negate<int>());
//...
Binder,可以将预定义的函数对象和其他数值结合
#include <iterator>
#include <functional>
#include <set>
#include <deque>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
set<int, greater<int>> coll{1,2,3,4,5,6,7};
deque<int> coll2;
transform(coll.begin(), coll.end(),
back_inserter(coll2),
bind(multiplies<int>(),_1,10));
replace_if(coll2.begin(), coll2.end(),
bind(equal_to<int>(), _1, 70),
34);
return 0;
}
对于bind(multiplies(),_1,10),定义一个函数对象,并将第一个参数乘以10
auto f = bind(multiplies<int>(), _1, 10);
cout << f(99) << endl;
也可以和自定义的函数对象结合
#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>
using namespace std;
using namespace std::placeholders;
template <typename T>
class MyEqual
{
public:
bool operator()(T a, T b)
{
return a == b;
}
};
int main(int argc, char* argv[])
{
vector<int> a{1,2,3,4,5,6,7};
auto count = count_if(a.begin(), a.end(), bind(MyEqual<int>(), _1, 1));
return 0;
}