zoukankan      html  css  js  c++  java
  • 第10课 std::bind和std::function(1)_可调用对象

    1. 几种可调用对象(Callable Objects)

    (1)普通函数指针类成员的函数指针

    (2)具有operator()成员函数的类对象(仿函数)。如c++11中的std::function类模板,本质上就是一个仿函数

    (3)可被转换为函数指针的类对象(需要重载类型转换操作符operator type(),其中type为目标类型:函数指针。这种写法与不带参的type operator()(void)效果等价)。

    【编程实验】可调用对象示例

    //test1.cpp

    #include <iostream>
    using namespace std;
    
    //1. 普通函数
    void func(void)
    {
    }
    
    //2. 仿函数对象
    struct Foo
    {
        void operator()(void)
        {
            
        }
    };
    
    //3. 可转换为函数指针的类
    struct Bar
    {
        using fr_t = void(*)(void); //function return type: 函数返回值类型
        
        static void func(void)
        {    
        }
        
        //将类对象转化为函数指针。
        //(本质上还是仿函数,只不过返回值是函数指针(fr_t)。
        operator fr_t(void)  //重载类型转换操作符,效果上等价于fr_t operator()(void)
        {
             return func;
        }
    };
    
    //4. 类的成员函数
    struct Test
    {
        int m;
        void mem_func(void)
        {
            
        }
    };
    
    int main()
    {
        //1. 普通函数
        void(*pfunc)(void) = &func;
        pfunc();
        
        //2.仿函数
        Foo foo;
        foo();
        
        //3.可转换为函数指针的类
        Bar bar;
        bar();
        
        //4.类成员函数指针
        void(Test::*pmem_func)(void) = &Test::mem_func;
        int Test::*pm = &Test::m;
        
        Test t;
        (t.*pmem_func)();
        t.*pm = 123;
    
        return 0;
    }

    2. 仿函数类的分析(如: binder2nd<T>、less<T>)

    (1)以count_if算法的调用为例

     

      ①调用count_if函数时,要向其传入容器的相关迭代器以及第3个参数,该参数是一个判断表达式,在本例中是一个binder2nd类型的仿函数对象

      ②每次遍历时,会调用binder2nd仿函数对象的operator(),并向其传入(*first)参数。该对象内部保存着由外部传入的less仿函数对象的引用及该函数对象的第2个参数值40,它们分别保存在op变量和value变量中。

      ③在binder2nd仿函数对象的operator()中,会调用op仿函数 (less对象)并传入*first和40,从而可以判断*first和40的大小。

    (2)相关源代码以说明

    /***********************************************************************/
    //以下两个类模板主要用于定义函数参数和返回值的类型。让子类可以回答
    //函数适配器(adapter)可能向其询问的这两个问题。
    //只有一个参数的函数类模板
    template <class Arg, class Result>
    struct unary_function {
      typedef Arg argument_type;  //参数类型
      typedef Result result_type; //返回值类型
    };
    
    //两个参数的函数类模板
    template <class Arg1, class Arg2, class Result>
    struct binary_function {
      typedef Arg1 first_argument_type;  //第1个参数类型
      typedef Arg2 second_argument_type; //第2个参数类型
      typedef Result result_type;        //返回值类型
    };  
    
    /***********************************************************************/
    //统计指定范围的元素个数的算法
    template <class InputIter, class Predicate>
    typename iterator_traits<InputIter>::difference_type
    count_if(InputIter first, InputIter last, Predicate pred) {
      typename iterator_traits<InputIter>::difference_type n = 0; //计数器
      for ( ; first != last; ++first) 
        if (pred(*first))  //如果元素带入pred判断的结果为true,计数器加1
          ++n;
      return n;
    }
    
    /***********************************************************************/
    //仿函数对象:用于比较x和y的大小。x<y则返回真,否则假。
    //注意:该类继承自binary_function类,表达他有两个参数和一个返回值。
    template <class T>
    struct less : public binary_function<T, T, bool> 
    {
      bool operator()(const Tp& x, const T& y) const { return x < y; }
    };
    
    
    //仿函数类:用于将可调用对象(如函数)以及其参数保存并封装成一个类
    //注意:该类继承自unary_function类,表示只有一个参数和返回值。
    //      binder2nd顾名思义就是要将参数绑定在op的第2个参数上,所以要求
    //      Operation这个类必须继承自binary_function。
    template <class Operation> 
    class binder2nd
      : public unary_function<typename Operation::first_argument_type,
                              typename Operation::result_type> {
    protected:
      Operation op; //可调用对象(如函数)
      
      //Operation类必须继承自binary_function类,才能回答下列关于第2个参数类型的提问。
      typename Operation::second_argument_type value;//函数的第2个参数
    public:
      //构造函数
      binder2nd(const Operation& x,
                const typename Operation::second_argument_type& y) 
          : op(x), value(y) {}
      typename Operation::result_type
      operator()(const typename Operation::first_argument_type& x) const {
        return op(x, value); //转向调用被保存对象的operator()函数
      }
    };
    
    /***********************************************************************/
    //辅助函数,让用户间接地,当然也更方便调用binder2nd(op,x);
    //如果直接调用,如binder2nd<Operation>(less<int>(), 40)则还需要填写出Operation,根据
    //binder2nd模板的定义,该值就是op的类型,此例中即less<int>的类型,这不好判断。
    //而如果用以下的bind2nd函数,可以让编译器根据op(即less<int>)自动推导出Operation类型。
    template <class Operation, class T>
    inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) 
    {
      typedef typename Operation::second_argument_type arg2_type;
      return binder2nd<Operation>(op, arg2_type(x));
    }
    bind2nd函数模板、binder2nd类模板等

    3. binder2nd类模板和bind2nd函数模板(重点)

    (1)binder2nd类模板

      ①重载了operator()操作符,所以该类的对象是一个函数对象(仿函数)

      ②该类用于保存外部传入的函数(或仿函数)以及其参数。它是一个Wrapper类,用于包装和改造传入的函数(或仿函数),然后形成一个新的仿函数对象

      ③继承自unary_function模板,表示该类最终绑定外部函数及参数后,仍然是一个函数对象(仿函数)。但新函数对象与传入的函数相比,其参数个数减少为1个

    (2)bind2nd函数模板

      ①bind2nd是一个函数模板,用于简化对binder2nd的操作。

      ②函数的返回值是一个binder2nd对象,该对象是个函数对象(仿函数),也是一个可调用对象。

    (3)bind1st、bind2nd函数模板

      ①bind1st用于将参数绑定在被绑定函数的第1个参数上,返回新的函数对象。

      ②bind2nd用于将参数绑定在被绑定函数的第2个参数上,返回新的函数对象。

  • 相关阅读:
    灵活读取Configuration文件
    Web页面访问权限
    母版页(MasterPage),你真得了解了吗?
    基于对象和面向对象
    [翻译]Silverlight 3中的tooltip
    2009年自我总结
    W3C CSS Validator 更喜欢CSS文件以一个class开头而不是注释?
    【翻译】读取文本文件(txt、csv、log、tab、fixed length)(上)
    1z0062 题库解析2
    1z0062 题库解析3
  • 原文地址:https://www.cnblogs.com/5iedu/p/7633288.html
Copyright © 2011-2022 走看看