STL學習筆記二(仿函式)
簡介:<functional>文件包含的大量的模版類unary_function,binary_function,plus,miuns, multiplies, divides, modulus, negate, equal_to, not_equal_to, greater, less, greater_equal, less_equal, logical_and, logical_or, logical_not, unary_negate, binary_negate,binder1st,binder2nd,pointer_to_unary_function,pointer_to_binary_function,mem_fun_t,mem_fun1_t,const_mem_fun_t,const_mem_fun1_t,mem_fun_ref_t,mem_fun1_ref_t,const_mem_fun_ref_t,const_mem_fun1_ref_t和模版函數not1, not2,bind1st,bind2nd,pointer_to_unary_function,pointer_to_binary_function,mem_fun_t,mem_fun1_t,const_mem_fun_t,mem_fun_ref,mem_fun1_ref。
一看到這麼多函數,你可能會有些吃驚,幸好作為一個程序員,我們不用記住並實現這些函數,隻需要熟練使用即可,其實仔細分析這些函數,發現這些函數都是成組具有共性的,比如加減乘除模版類plus,miuns,multiplies,modulus,異或操作logical_or,logical_not,logical_and,根據其名字很容易理解類或者函數的含義。
類模版:這裏的函數模版和模版類雖然多,但是實現都比較簡單,模版類中不包含任何數據項(頂多有個函數指針變量),是一些操作(構造函數,operator())或者映射,比如在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;
};
Argument_type和result_type是STL中的算法中需要的映射,如果不映射這兩個類型,函數對象將無法使用,比如bind1st。一種良好的習慣就是我們自定義的函數對象時,從unary_function或binary_function繼承而來,這樣可以保證函數對象有良好的移植性和正確行,模版類unary_function中的第一個類型是函數的傳遞參數,最後類型是函數的返回類型,對於binary_function也是最後一個類型是函數的返回類型。
我們看看plus實現
// TEMPLATE STRUCT plus
template<class _Ty>
struct plus
: public binary_function<_Ty, _Ty, _Ty>
{ // functor for operator+
_Ty operator()(const _Ty& _Left, const _Ty& _Right) const
{ // apply operator+ to operands
return (_Left + _Right); // minus(-)…
}
};
這是一個函數對象,就是從binary_function類繼承而來,我們也將看到STL中的其他函數對象也都是從unary_function和binary_function中繼承而來。在使用容器時,我們常使用的greater,less實現也非常簡單,如下
// TEMPLATE STRUCT greater
template<class _Ty>
struct greater
: 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);
}
};
源碼麵前,了無秘密可言。
Not1,not2函數:但是並不是每個模版函數都容易理解,看看not1,not2這兩個模版函數,是把函數參數個數為1或者2的函數的返回值由int,char或者其他類型改為bool類型。這裏not就是NOT(否)(剛開始的時候我還以為是note,想了半天沒有明白)
// TEMPLATE CLASS unary_negate
template<class _Fn1>
class unary_negate
: public unary_function<typename _Fn1::argument_type, bool>
// 注意typename _Fn1::argument_type,如你所見,這裏的_Fn1是一個函數對象
{ // functor adapter !_Func(left)
public:
explicit unary_negate(const _Fn1& _Func)
: _Functor(_Func)
{ // construct from functor
}
bool operator()(const typename _Fn1::argument_type& _Left) const
{ // apply functor to operand
return (!_Functor(_Left));
}
protected:
_Fn1 _Functor; // the functor to apply
};
// TEMPLATE FUNCTION not1
template<class _Fn1> inline
unary_negate<_Fn1> not1(const _Fn1& _Func)
{ // return a unary_negate functor adapter
return (std::unary_negate<_Fn1>(_Func));
}
注意typename _Fn1::argument_type,這說明_Fn1是一個從unary_function繼承而來的函數對象,或者函數對象中自己建立了argument_type,result_type映射。進一步說not1與not2的參數為函數對象,假如我們現在隻有一個函數,比如C中的strcmp函數(字符串比較函數,分別返回-1,0,1),能否使用not2模版函數,答案是肯定的,如下所示
Not2(ptr_fun(strcmp))
稍後我們解釋ptr_fun模版函數
bind1st,bind2nd函數:
Bind1st,bind2nd正如其名字,分別為綁定1第一個參數,綁定第二個參數,代碼如下
// 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;
// 以上代碼的作用就是rebind
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));
}
result_type operator()(argument_type& _Left) const
{ // apply functor to operands
return (op(_Left, value));
}
protected:
_Fn2 op; // the functor to apply
typename _Fn2::second_argument_type value; // the right operand
};
// 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));
}
比如bind1st(ptr_fun(strcmp),”abc”);
這樣,strcmp輸入的第一個參數是字符串abc,同理bind2nd,
比如bind2nd(ptr_fun(strcmp),”abc”);
這樣,strcmp輸入的第二個參數是字符串abc。
Par_fun, mem_fun, mem_fun_ref函數
終於要掀開ptr_fun的神秘麵紗了,當我們已經有一個成熟的函數時,比如C中的strcmp,並且不想再寫我們自己的函數對象,隻是想直接應用strcmp,在STL中可以使用嘛?答案是肯定的,不過需要一些變化而已,因為我們知道,STL中的算法是基於函數對象的,所以我們隻要把函數轉化為函數對象即可,這聽起來是一個複雜的過程,其實很簡單,如下
// TEMPLATE CLASS pointer_to_binary_function
template<class _Arg1,
class _Arg2,
class _Result>
class pointer_to_binary_function
: public binary_function<_Arg1, _Arg2, _Result>
{ // functor adapter (*pfunc)(left, right)
public:
explicit pointer_to_binary_function(
_Result (__cdecl *_Left)(_Arg1, _Arg2))
: _Pfun(_Left)
{ // construct from pointer
}
_Result operator()(_Arg1 _Left, _Arg2 _Right) const
{ // call function with operands
return (_Pfun(_Left, _Right));
}
protected:
_Result (__cdecl *_Pfun)(_Arg1, _Arg2); // the function pointer
};
定義一個二元函數對象,此函數對象實現對函數指針的封裝
// TEMPLATE FUNCTION ptr_fun
template<class _Arg1,
class _Arg2,
class _Result> inline
pointer_to_binary_function<_Arg1, _Arg2, _Result>
ptr_fun(_Result (__cdecl *_Left)(_Arg1, _Arg2))
{ // return pointer_to_binary_function functor adapter
return (std::pointer_to_binary_function<_Arg1, _Arg2, _Result>(_Left));
}
調用ptr_fun函數,返回一個封裝函數指針的二元函數對象。ptr_fun函數是一個重載函數,根據函數指針類型的不同,也可以返回一個一元函數對象。這樣就可以把一個普通的函數轉換為一個函數對象了。
如果現在你想把一個類的成員函數,轉化為函數對象,那麼怎麼辦,幸好有了mem_fun,字麵意思也很容易理解,成員函數。對於mem_fun有更多的重載函數,我隻列舉其中一個。
// TEMPLATE CLASS const_mem_fun_t
template<class _Result,
class _Ty>
class const_mem_fun_t
: public unary_function<const _Ty *, _Result>
{ // functor adapter (*p->*pfunc)(), const *pfunc
public:
explicit const_mem_fun_t(_Result (_Ty::*_Pm)() const)
: _Pmemfun(_Pm)
{ // construct from pointer
}
_Result operator()(const _Ty *_Pleft) const
{ // call function
return ((_Pleft->*_Pmemfun)());
}
private:
_Result (_Ty::*_Pmemfun)() const; // the member function pointer
};
// TEMPLATE FUNCTION mem_fun
template<class _Result,
class _Ty> inline
mem_fun_t<_Result, _Ty> mem_fun(_Result (_Ty::*_Pm)())
{ // return a mem_fun_t functor adapter
return (std::mem_fun_t<_Result, _Ty>(_Pm));
}
這就是成員函數作為回調函數的技巧,一般來說,我們總是說把一個類的成員函數修改為成員函數的方法是把此函數改為static函數。因為這種方法可以引用的C API方式的回調函數中去,而今天我們看到mem_fun函數其實是把類成員作為回調函數。至於mem_fun_ref這裏就不在說明了,他和mem_fun的區別在這個函數內
_Result operator()(_Ty& _Left)
{ // call function
return ((_Left.*_Pmemfun)());
}
不知道,STL為什麼非要分為指針類型和引用類性,可能有些算法即使用了指針類型,又使用了引用類型吧。
正如你所見,這裏沒有提到mem_fun1_ref,mem_fun1_t等,這是因為這些模版類和模版函數是STL中的保留(殘留)類和函數,在C++的下個版本可能會去除。
最後更新:2017-04-02 00:06:15