zoukankan      html  css  js  c++  java
  • STL标准库-仿函数与仿函数适配器

    技术在于交流、沟通,本文为博主原创文章转载请注明出处并保持作品的完整性

    概要:

    1.仿函数

    2.bind2nd()

    3.not1()

    4.bind()


    仿函数的实现:声明一个类,重载它的operator call ("()"操作符)

    template<typename T>
    class lineFeed
    {
    public:
        void operator()(const T &x)
        {
            cout<<x<<endl;
        }
    };

    仿函数只为算法服务,但是像上面这种声明方式,虽然在有些时候可以使用,但是却不能融入STL,因为有可能在"仿函数适配器"上出现编译错误,下面我们来看看STL中的仿函数.

    仿函数的使用可以看一下我的上一节博客,虽然都没有继承自 binary_function<T,T,bool>,unary_function<T,bool>...

    STL中的仿函数大致可以分为三个类

     1.算数类

      template<typename _Tp>
        struct plus : public binary_function<_Tp, _Tp, _Tp>
        {
          _Tp
          operator()(const _Tp& __x, const _Tp& __y) const
          { return __x + __y; }
        };
    
    
    
      template<typename _Tp>
        struct minus : public binary_function<_Tp, _Tp, _Tp>
        {
          _Tp
          operator()(const _Tp& __x, const _Tp& __y) const
          { return __x - __y; }
        };
    ...

     2.逻辑运算类

      template<typename _Tp>
        struct logical_and : public binary_function<_Tp, _Tp, bool>
        {
          bool
          operator()(const _Tp& __x, const _Tp& __y) const
          { return __x && __y; }
        };
    
    
      template<typename _Tp>
        struct logical_or : public binary_function<_Tp, _Tp, bool>
        {
          bool
          operator()(const _Tp& __x, const _Tp& __y) const
          { return __x || __y; }
        };
    
      template<typename _Tp>
        struct logical_not : public unary_function<_Tp, bool>
        {
          bool
          operator()(const _Tp& __x) const
          { return !__x; }
        };
    
    ...

    3.相对关系类

      template<typename _Tp>
        struct equal_to : public binary_function<_Tp, _Tp, bool>
        {
          bool
          operator()(const _Tp& __x, const _Tp& __y) const
          { return __x == __y; }
        };
    
    
      template<typename _Tp>
        struct not_equal_to : public binary_function<_Tp, _Tp, bool>
        {
          bool
          operator()(const _Tp& __x, const _Tp& __y) const
          { return __x != __y; }
        };
    ...

    我们会看到所有的STL仿函数都需要继承binary_function<T, T, bool>(两个参数)或者unary_function<T, bool>(一个参数),只有继承自这两种父类,你声明的仿函数才可以融入STL

    上面提及到仿函数适配器, 如果不继承上面这两种类, 在执行到某个阶段会报错.

    下面是binary_function<>与unary_function<>的源码

    binary_function<T, T, bool>,发现binary_function<>

      template<typename _Arg1, typename _Arg2, typename _Result>
        struct binary_function
        {
          /// @c first_argument_type is the type of the first argument
          typedef _Arg1     first_argument_type; 
    
          /// @c second_argument_type is the type of the second argument
          typedef _Arg2     second_argument_type;
    
          /// @c result_type is the return type
          typedef _Result     result_type;
        };

    unary_function<T, bool>

      template<typename _Arg, typename _Result>
        struct unary_function
        {
          /// @c argument_type is the type of the argument
          typedef _Arg     argument_type;   
    
          /// @c result_type is the return type
          typedef _Result     result_type;  
        };

    那么为什么没有继承自这两个类,会报错呢?我们看上面的源码,他们的内部都有typedef,这几个typedef是为了提取算法传进变量,以供仿函数适配器使用,如果你没有继承这两种类,那么在算法调用仿函数时,仿函数适配器提取不出这几个变量,那么就会报错.

    写仿函数,一定要继承上面这两个类.


    仿函数适配器

    下面介绍几个仿函数用的适配器函数

    1.bind2nd().它的作用是绑定仿函数的第二参数

    它本身就是一个仿函数的适配器,它的作用是为我简化binder2nd(),因为binder2nd()的参数类型比较复杂,我们在调用的时候回很不方便.

    先看一个测试函数

    namespace wzj007 {
        void test_Function()
        {
            vector<int> myVector = {10,20,40,70,90,100};
            cout << count_if(myVector.begin(), myVector.end(),bind2nd(less<int>(),40));//返回vector中value小于40的个数
        }
    }

    现在解释一下bind2nd()函数的作用: 它将40 这个变量绑定在less()函数的第二参数上,less函数返回第一参数是否小于第二参数,那么绑定后的less()函数就应该返回 传入的参数是否小于40

    小于40的value有10,20.那么输出的结果应该是2.那么具体是怎么绑定上的呢

    我们先看一下less()的源码

    template <class T> struct less {
      bool operator() (const T& x, const T& y) const {return x<y;}
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef bool result_type;
    };

    他也是一个仿函数,但是却发现它没有继承自binary_function<T, T, bool>,因为他自己声明了那三种typedef,他需要两个两个参数,x和y,返回 x是否小于y.

    那么 bind2nd(less<int>(),40) 他的含义就应该是 比较传进参数x,是否小于40(y); 

    下面我们来看一下是怎么绑定上的

     这是bind2nd()的源代码

      template<typename _Operation, typename _Tp>
        inline binder2nd<_Operation>
        bind2nd(const _Operation& __fn, const _Tp& __x)
        {
          typedef typename _Operation::second_argument_type _Arg2_type;//这有一个typedef 它提取出operation的第二参数,同时可以检测第二参数类型
          return binder2nd<_Operation>(__fn, _Arg2_type(__x));
        } 

     这个binder2nd()的源码,binder2nd()是一个仿函数,他的作用是绑定仿函数的的第二参数

    template<typename _Operation>
        class binder2nd
        : public unary_function<typename _Operation::first_argument_type,
                    typename _Operation::result_type>
        {
        protected:
          _Operation op;//取出传进的仿函数
          typename _Operation::second_argument_type value;//取出第二参数,同时可检测第二参数类型
    
        public:
          binder2nd(const _Operation& __x,
            const typename _Operation::second_argument_type& __y)
          : op(__x), value(__y) { }//构造函数(在bind2nd()函数中调用),初始化自身成员变量 op,value
    
          typename _Operation::result_type
          operator()(const typename _Operation::first_argument_type& __x) const
          { return op(__x, value); }//重载operator() 在函数count_if()中被调用
    
          // _GLIBCXX_RESOLVE_LIB_DEFECTS
          // 109.  Missing binders for non-const sequence elements
          typename _Operation::result_type
          operator()(typename _Operation::first_argument_type& __x) const
          { return op(__x, value); }//非const
        } _GLIBCXX_DEPRECATED;

     这是count_if()的源码

    template <class Inputerator, class Outputerator, class Predicate>
    typename iterator_traits<Inputerator>::difference_type;
    count_if(Inputerator first, Inputerator last, Predicate pred)
    {
        typename iterator_traits<Inputerator>::difference_type;
        for( ; first != last; ++first)
            if(pred(*first)) //这个地方会调用函数pred(*first), 重上面我们可以看到 pred绑定的函数是binder2nd()中的 operator()函数,那么此时的pred就应该是less函数
                ++n;
        return n;
    }

    现在band1st()的含义我们应该可以猜到了吧.


    not1()将函数的返回值取否

    他也是一种仿函数适配器,简化我们调用unary_negate()

        void test_Function_bind2nd()
        {
            vector<int> myVector = {10,20,40,70,90,100};
            cout << count_if(myVector.begin(), myVector.end(),not1(bind2nd(less<int>(),40)));//
        }

    调用not1()之前返回的是小于40的value有几个,那么取否后就应该是不小于40的value有几个: 4个(40,70,90,100)

    看一下是怎么关联上的

    not1()源码

      template<typename _Predicate>
        inline unary_negate<_Predicate>
        not1(const _Predicate& __pred)
        { return unary_negate<_Predicate>(__pred); }//调用unary_negate()的构造函数

     unary_negate()源码

      template<typename _Predicate>
        class unary_negate
        : public unary_function<typename _Predicate::argument_type, bool>
        {
        protected:
          _Predicate _M_pred;
    
        public:
          explicit
          unary_negate(const _Predicate& __x) : _M_pred(__x) { }//not1()调用构造函数初始化成员变量
    
          bool
          operator()(const typename _Predicate::argument_type& __x) const
          { return !_M_pred(__x); }//取反 count_if()调用其operator()
        };

    bind() C++11的绑定函数

    这些函数是C++11的一些变更函数,原来的函数仍然可以使用,从上面可以看出 bind2nd改名为bind();

    那么bind()具体怎么使用呢

    这份代码是我在http://www.cplusplus.com上拷下来的

    namespace wzj008 {
    
        // a function: (also works with function object: std::divides<double> my_divide;)
        double my_divide (double x, double y) {return x/y;}
    
        struct MyPair {
            double a,b;
            double multiply() {return a*b;}
        };
        void test_Function_bind()
        {
            using namespace std::placeholders;    // 使用placehloders _1(第一参数占位符), _2(第二参数占位符) ,_3(第三参数占位符)...
    
            // binding functions:
            auto fn_five = std::bind (my_divide,10,2);               // returns 10/2   将10 绑定在my_divide()函数的第一参数上, 2绑定在my_divide()函数的第二参数上 10/2 = 5
            std::cout << fn_five() << '
    ';                          // 5
    
            auto fn_half = std::bind (my_divide,_1,2);               // returns x/2    使用占位符_1 即将my_divide()函数的第一参数绑定为传入参数, 2绑定为my_divide()函数的第二参数
            std::cout << fn_half(10) << '
    ';                        // 5         10/2 = 5
    
            auto fn_invert = std::bind (my_divide,_2,_1);            // returns y/x    使用占位符_2 即_2为函数my_divide()的第二参数, _1为my_divide()的第一参数
            std::cout << fn_invert(10,2) << '
    ';                    // 0.2       10(_2)为第二参数    2(_1)第一参数    2/10 = 0.2
    
            auto fn_rounding = std::bind<int> (my_divide,_1,_2);     // returns int(x/y) 将fn_rounding()绑定为my_divide()函数
            std::cout << fn_rounding(10,3) << '
    ';                  // 3
    
            MyPair ten_two {10,2};                     //绑定成员变量
    
            // binding members:
            auto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply() 
            std::cout << bound_member_fn(ten_two) << '
    ';           // 20  将传进参数ten_two{10,2} 的10和2 分别赋给 a和b(a = 10, b = 2)  10 * 2 = 20
    
            auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.a   给a赋值  a = 10;
            std::cout << bound_member_data() << '
    ';                // 10
        }
    }

    参考侯捷<<STL源码剖析>>

  • 相关阅读:
    开篇有益-解析微软微服务架构eShopOnContainers(一)
    复杂而艰辛的重构之路--起步
    Visual Studio 我的插件
    【翻译】使用Visual Studio创建Asp.Net Core MVC (一)
    【翻译】使用Visual Studio在Azure上部署Asp.Net Core Web应用
    【翻译】在Visual Studio中使用Asp.Net Core MVC创建第一个Web Api应用(二)
    【翻译】在Visual Studio中使用Asp.Net Core MVC创建你的第一个Web API应用(一)
    【翻译】在Mac上使用VSCode创建你的第一个Asp.Net Core应用
    【翻译】Asp.net Core介绍
    新的一年订个小目标,比如每周更新1-2篇博文
  • 原文地址:https://www.cnblogs.com/LearningTheLoad/p/7594646.html
Copyright © 2011-2022 走看看