所谓的functor就是使用起来像函数一样的东西,如果你针对某个class进行operator() 【function call操作符】重载,它就成为一个仿函数,至于要成为一个可配接的仿函数,还需要做一些额外的努力。
为了拥有配接能力,每一个仿函数都必须定义自己的相应型别,就像迭代器如果要融入整个STL大家庭,也必须依照规定定义自己的5个相应型别一样。这些型别是为了让配接器能够获取相关仿函数的信息,相应的型别都只是一些typedef,其定义了函数的参数型别及返回值类型。
为了方便起见,stl_function.h中定义了两个classes,unary_function和binary_function,分别代表一元仿函数和二元仿函数。只要按需求选择继承某一个class,变自动拥有了那些相应型别,也就自动拥有了配接能力。相关源码如下:
template
struct unary_function {
typedef _Arg argument_type;
typedef _Result result_type;
};
template
struct binary_function {
typedef _Arg1 first_argument_type;
typedef _Arg2 second_argument_type;
typedef _Result result_type;
};
具有配接能力的class,就能够和配接器一起使用了。一些常用的配接器如下:
bind1st, bind2nd, not1, not2, ptr_fun, mem_fun,men_fun_ref, mem_fun1, mem_fun1_ref,其中bind1st和bind2nd用于绑定函数的第一个参数和第二个参数,not1和not2分别适用于一元函数和二元函数。以bind1st为例,我们可以通过其源码进一步了解配接能力的概念:
template
class binder1st
:public unary_function {
protected:
_Operation op;
typename _Operation::first_argument_type value;
public:
binder1st(const _Operation& __x,
const typename _Operation::first_argument_type& __y)
: op(__x), value(__y) {}
typename _Operation::result_type
operator()(const typename _Operation::second_argument_type& __x)const {
return op(value, __x);
}
};
template
inline binder1st<_Operation>
bind1st(const _Operation& __fn, const_Tp& __x)
{
typedef typename _Operation::first_argument_type _Arg1_type;
return binder1st<_Operation>(__fn, _Arg1_type(__x));
}
比较容易混淆的是ptr_fun,mem_fun, mem_fun_ptr三个仿函数,下面进行说明:
1) ptr_fun:将函数指针进行包装,使其具有配接能力,其源码如下:
template
class pointer_to_unary_function : publicunary_function<_Arg, _Result> {
protected:
_Result (*_M_ptr)(_Arg); //封装了函数指针,继承了unary_function,具有配接能力
public:
pointer_to_unary_function() {}
explicitpointer_to_unary_function(_Result (*__x)(_Arg)) : _M_ptr(__x) {}
_Result operator()(_Arg __x) const { return _M_ptr(__x); }
};
template
inline pointer_to_unary_function<_Arg,_Result> ptr_fun(_Result (*__x)(_Arg))
{
return pointer_to_unary_function<_Arg,_Result>(__x);
}
p.s:这是一元函数的形式,stl还重载了二元函数的ptr_fun,这边不列举了,有兴趣的参考stl_function.h。
2) mem_fun与men_fun_ref:用于将成员函数当做仿函数来使用,使成员函数可以搭配各种泛型算法。当容器元素为T&或T*,而我们又以虚函数作为仿函数,便可以由此实现多态调用。
其区别在于函数实例对象类型不同,如果是T&类型,则使用mem_fun_ref;如果是T*类型,则使用mem_fun。
下面列举源码加以说明(以非const类型为例):
template
class mem_fun_t : publicunary_function<_Tp*,_Ret> {
public:
explicit mem_fun_t(_Ret (_Tp::*__pf)()) : _M_f(__pf) {} //封装了成员函数指针
_Ret operator()(_Tp* __p) const { return (__p->*_M_f)(); } //实例对象为指针类型
private:
_Ret (_Tp::*_M_f)();
};
template
inline mem_fun_t<_Ret,_Tp>mem_fun(_Ret (_Tp::*__f)())
{return mem_fun_t<_Ret,_Tp>(__f); }
template
class mem_fun_ref_t : public unary_function<_Tp,_Ret>{
public:
explicit mem_fun_ref_t(_Ret (_Tp::*__pf)()) : _M_f(__pf) {}
_Ret operator()(_Tp& __r) const { return (__r.*_M_f)(); } //实例对象为引用类型
private:
_Ret (_Tp::*_M_f)();
};
template
inline mem_fun_ref_t<_Ret,_Tp>mem_fun_ref(_Ret (_Tp::*__f)())
{return mem_fun_ref_t<_Ret,_Tp>(__f); }
下面是关于上述三个函数使用的一个实例:
vector
vector
vector
class Shape
{
public:
Shape() {cout << "Shape construct" << endl;}
virtual ~Shape() {cout << "Shape destuct" < vec;
vec.push_back(new Rect);
vec.push_back(new Circle);
for_each(vec.begin(), vec.end(), mem_fun(&Shape::display)); //应使用mem_fun,因为实例对象是Shape*
for(vector::iterator iter = vec.begin(); iter !=vec.end(); ++iter)
{
cout < vec1; //无法实现多态
vec1.reserve(3);
Rect rect1;
vec1.push_back(rect1);
Circle circle1;
vec1.push_back(circle1);
for_each(vec1.begin(), vec1.end(), mem_fun_ref(&Shape::display)); //应使用mem_fun_ref,因为实例对象是Shape
//vector vec2; //无法通过编译
return 0;
}