STL学习笔记二(仿函式)

    技术2022-05-11  70

    简介:<functional>文件包含的大量的模版类unary_function,binary_function,plusmiuns, multiplies, divides, modulus, negate, equal_to, not_equal_to, greater, less, greater_equal, less_equal, logical_and, logical_or, logical_not, unary_negate, binary_negatebinder1stbinder2ndpointer_to_unary_functionpointer_to_binary_functionmem_fun_tmem_fun1_tconst_mem_fun_tconst_mem_fun1_tmem_fun_ref_tmem_fun1_ref_tconst_mem_fun_ref_tconst_mem_fun1_ref_t和模版函数not1, not2bind1stbind2ndpointer_to_unary_functionpointer_to_binary_functionmem_fun_tmem_fun1_tconst_mem_fun_tmem_fun_refmem_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_typeresult_typeSTL中的算法中需要的映射,如果不映射这两个类型,函数对象将无法使用,比如bind1st。一种良好的习惯就是我们自定义的函数对象时,从unary_functionbinary_function继承而来,这样可以保证函数对象有良好的移植性和正确行,模版类unary_function中的第一个类型是函数的传递参数,最后类型是函数的返回类型,对于binary_function也是最后一个类型是函数的返回类型。

    我们看看plus实现

         // TEMPLATE STRUCT plustemplate<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_functionbinary_function中继承而来。在使用容器时,我们常使用的greaterless实现也非常简单,如下

             // TEMPLATE STRUCT greatertemplate<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);         }     };

    源码面前,了无秘密可言。

    Not1not2函数:但是并不是每个模版函数都容易理解,看看not1,not2这两个模版函数,是把函数参数个数为1或者2的函数的返回值由intchar或者其他类型改为bool类型。这里not就是NOT(否)(刚开始的时候我还以为是note,想了半天没有明白)

             // TEMPLATE CLASS unary_negatetemplate<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 not1template<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_typeresult_type映射。进一步说not1not2的参数为函数对象,假如我们现在只有一个函数,比如C中的strcmp函数(字符串比较函数,分别返回-101),能否使用not2模版函数,答案是肯定的,如下所示

    Not2(ptr_fun(strcmp))

    稍后我们解释ptr_fun模版函数

    bind1st,bind2nd函数:

    Bind1stbind2nd正如其名字,分别为绑定1第一个参数,绑定第二个参数,代码如下

    // TEMPLATE CLASS binder2ndtemplate<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 bind2ndtemplate<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_functiontemplate<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_funtemplate<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_ttemplate<class _Result,     class _Ty>     class const_mem_fun_t         : public unary_function<const _Ty *, _Result>     {    // functor adapter (*p->*pfunc)(), const *pfuncpublic:     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_funtemplate<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_refmem_fun1_t等,这是因为这些模版类和模版函数是STL中的保留(残留)类和函数,在C++的下个版本可能会去除。

     

    最新回复(0)