仿函数,又叫做函数对象,是一个重载了"()"运算符的struct,是STL(标准模板库)六大组件(容器、配置器、迭代器、算法、配接器、仿函数)之一;仿函数虽然小,但却极大的拓展了算法的功能,几乎所有的算法都有仿函数版本。例如,查找算法find_if就是对find算法的扩展,标准的查找是两个元素向等就找到了,但是什么是相等在不同情况下却需要不同的定义,如地址相等,地址和邮编都相等,虽然这些相等的定义在变,但算法本身却不需要改变,这都多亏了仿函数。
看个简单的例子:
struct D {
D(int i=0){num=i;}
int num;
};
struct print_D{
void operator()(const D* d)const{
cout<<"I am D. my num="<<d->num<<endl;
}
};
int main()
{
vector<D*> V;
V.push_back(new D(1));
V.push_back(new D(2));
V.push_back(new D);
V.push_back(new D(3));
for_each(V.begin(), V.end(), print_D());
}
编译输出:
I am D. my num=1
I am D. my num=2
I am D. my num=0
I am D. my num=3
如果使用mem_fun,会方便很多:
struct D {
D(int i=0){num=i;}
void print() { cout << "I'm a D. my num=" << num<< endl; }
int num;
};
int main()
{
vector<D*> V;
V.push_back(new D(1));
V.push_back(new D(2));
V.push_back(new D);
V.push_back(new D(3));
for_each(V.begin(), V.end(), mem_fun(&D::print));
}
mem_fun对于一些多态的虚函数也十分有用
struct B {
virtual void print() = 0;
};
struct D1 : public B {
void print() { cout << "I'm a D1" << endl; }
};
struct D2 : public B {
void print() { cout << "I'm a D2" << endl; }
};
int main()
{
vector<B*> V;
V.push_back(new D1);
V.push_back(new D2);
V.push_back(new D2);
V.push_back(new D1);
for_each(V.begin(), V.end(), mem_fun(&B::print));
}
仿函数之所以叫做函数对象,是因为仿函数都是定义了()函数运算操作符的类。例如,STL自带的仿函数equal_to<class Tp>定义为:
template <class _Tp>
struct equal_to : public binary_function<_Tp,_Tp,bool>
{
bool operator()(const_Tp&__x,const_Tp&__y) const { return __x==__y; }
};
在算法内部调用此操作符,如find_if:
template <class_RandomAccessIter,class_Predicate>
_STLP_INLINE_LOOP _RandomAccessIter __find_if(_RandomAccessIter __first, _RandomAccessIter __last,_Predicate __pred,const random_access_iterator_tag &)
{
_STLP_DIFFERENCE_TYPE(_RandomAccessIter) __trip_count = (__last - __first) >> 2;
for ( ; __trip_count > 0 ; --__trip_count) {
if (__pred(*__first)) return __first;
++__first;
…
//以下略
}
仿函数的可配接性
仿函数的可配接性是指仿函数能够与其它仿函数配接在一起实现新的功能,如不小于60,可以利用STL自带的not1<int>和less<int>配接而成:not1(bind2nd(less<int>(), 12))。
一般而言,通用函数也可以作为仿函数参数传递给算法,但其区别在于“通用函数不具有可配接性”。是否定义成仿函数都具有配接性了呢?也不尽然!只有从unary_function或者binary_funcion继承的仿函数才有配接性。这是为什么呢?
其奥妙在于模板类常见的类型定义,可配接性的关键就在于这些类型定义;如binary_function:
template <class _Arg1, class _Arg2, class _Result>
struct binary_function {
typedef _Arg1 first_argument_type;
typedef _Arg2 second_argument_type;
typedef _Result result_type;
};
在STL的适配器中会自动使用到这些类型定义,所以必须声明这些类型。
把通用函数转换为仿函数
STL的实现也考虑到会将通用函数作为仿函数来使用,为了保证这些函数的可配接性,即把这些函数转换为仿函数使用,STL也提供了相应的适配器ptr_fun1_base,ptr_fun2_base,其原理也是重载函数调用操作符,在仿函数对象构造时把通用函数作为参数传入,如:
template <class _Arg, class _Result>
class pointer_to_unary_function : public unary_function<_Arg, _Result>
{
protected:
//函数原型
_Result (*_M_ptr)(_Arg);
public:
pointer_to_unary_function() {}
//构造时把函数指针传入
explicit pointer_to_unary_function(_Result (*__x)(_Arg)) : _M_ptr(__x) {}
//()函数运算操作符重载,执行函数功能
_Result operator()(_Arg __x) const { return _M_ptr(__x); }
};