前言
仿函数的可配接性是指仿函数能够与其它仿函数配接在一起实现新的功能,如不小于60,该需求可以利用STL自带的not1<int>和less<int>配接而成,代码实现如下:
not1(bind2nd(less<int>(), 60))
但是有时又需要我们自定义仿函数,又或者类成员函数与仿函数绑定、一般函数与仿函数一起绑定等,针对这些场景STL给出了额外的函数配接器或者给出技术要求,在函数配接器博文中就介绍了STL额外的两种函数配接器,包括成员函数配接器,一般函数配接器,这两种函数配接器的实现原理bind2nd大致相当,核心都进行了operator()重载操作
“可配接性”分析
为了能够达到可配接性,就必须提供型别成员(type member) 来反映其参数和返回值;STL提供了binary_function,unary_function这两个结构体;它们具体结构如下:
// TEMPLATE STRUCT unary_function template<class _Arg, class _Result> struct unary_function { // base class for unary functions typedef _Arg argument_type; typedef _Result result_type; }; // TEMPLATE STRUCT binary_function template<class _Arg1, class _Arg2, class _Result> struct binary_function { // base class for binary functions typedef _Arg1 first_argument_type; typedef _Arg2 second_argument_type; typedef _Result result_type; };
这两个结构通过模板技术在内部进行了型别的重命名,STL预定的仿函数都继承于这两个结构体,因此预定义的仿函数就获得了用户指定的类型,因此预定义的仿函数都具备“可配接性”。若我们自定义的仿函数需要和函数配接器结合,也必须继承这两者之一;
bind2nd原理剖析
我们以如下代码分析bind2nd的实现过程:
int nNumber1 = count_if(Container.begin(), Container.end(), bind2nd(less<int>(), 60));
在配接器bind2nd中,参数1是less<int>() 生成仿函数对象副本,其参数是int类型;参数2是value60;
less源码如下:template<class _Ty> struct less: public binary_function<_Ty, _Ty, bool> { // functor for operator< bool operator()(const _Ty& _Left, const _Ty& _Right) const { // apply operator< to operands return (_Left < _Right); } };
仿函数less继承于binary_function模板,并将less的比较类型传递给了binary_function模板,在binary_function模板类中对_Arg1,_Arg2进行了类型重命名,分别是first_argument_type 和second_argument_type,这里都是int类型,和less模板参数一致,result_type是bool类型。
bind2nd源码如下:
// TEMPLATE FUNCTION bind2nd template<class _Fn2, class _Ty> inline binder2nd<_Fn2> bind2nd(const _Fn2& _Func, const _Ty& _Right) { // return a binder2nd functor adapter typename _Fn2::second_argument_type _Val(_Right);//定义变量并初始化 return (std::binder2nd<_Fn2>(_Func, _Val)); }
在bind2nd函数体内定义了临时变量_Val并初始化,其类型是仿函数less中定义的second_argument_type,也就是int类型,配接器函数bind2nd返回值是仿函数binder2nd对象副本,其成员是less functor和value 60;
binderd2nd的实现如下:
// TEMPLATE CLASS binder2nd template<class _Fn2> class binder2nd: public unary_function<typename _Fn2::first_argument_type, typename _Fn2::result_type> { // functor adapter _Func(left, stored) public: typedef unary_function<typename _Fn2::first_argument_type, typename _Fn2::result_type> _Base; typedef typename _Base::argument_type argument_type; typedef typename _Base::result_type result_type; binder2nd(const _Fn2& _Func, const typename _Fn2::second_argument_type& _Right):op(_Func), value(_Right) { // construct from functor and right operand } result_type operator()(const argument_type& _Left) const { // apply functor to operands return (op(_Left, value));//返回less仿函数临时对象 } result_type operator()(argument_type& _Left) const { // apply functor to operands return (op(_Left, value));//返回less仿函数临时对象 } protected: _Fn2 op; // the functor to apply 返回值是是仿函数对象即less仿函数 typename _Fn2::second_argument_type value; // the right operand };
现在分析count_if的实现,源码如下:
template<class _InIt, class _Pr> inline typename iterator_traits<_InIt>::difference_type _Count_if(_InIt _First, _InIt _Last, _Pr _Pred) { // count elements satisfying _Pred typename iterator_traits<_InIt>::difference_type _Count = 0; for (; _First != _Last; ++_First) { if (_Pred(*_First))//调用binder2nd中的operator()函数 { ++_Count; } } return (_Count); }_Pred是仿函数binder2nd的副本,_Pred(*_First)会调用binder2nd的operator(), op是less仿函数对象,第一参数是容器中的元素,第二个参数是60,count_if会对每一元素都做一遍判断,从而统计符合要求的元素个数。
bind2nd原理地图
下图展示了bind2nd配接器实现过程,如何完成仿函数和仿函数的配接过程。
Step1:将仿函数less和value 60固化到仿函数binder2nd内部,称为仿函数binder2nd的成员函数
Step2:bind2nd函数返回binder2nd的对象,并传递给count_if算法,称为第三个参数
Step3:在count_if()算法内部调用binder2nd operater()实现,从而完成符合要求的数据统计