美文网首页
小王职场记STL(3)lambda表达式的本质

小王职场记STL(3)lambda表达式的本质

作者: 小王同学加油 | 来源:发表于2019-01-31 15:53 被阅读41次

    上篇文章回顾:

    STL理解(1)容器

    STL理解(2) 算法

    经过上面你了解 ,你应该知道了

    1. stl使用大量inline函数,inline函数定义光明正大放到头文件保证不会出错
      ------想象普通函数定义为什么不可以,想一想 stl的普通函数为什么可以?

    2. 因为放到头文件 模板+重载 构成预编译的多态

    1548314223531.png

    本节 介绍的是另一一个点,stl 为什么提供强大的适配能力 。

    提供了函数对象,也就是lambda表达式的本质

    看一行代码

    std::vector<int> vec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    //计算容器中小于等于3的元素个数
    cout << count_if(vec.begin(), vec.end(), bind2nd(less_equal<int>(), 3));
    int count  = std::count_if(vec.begin(), vec.end(), [](int x) {return x >  3;})
    

    看不懂没 关系 继续分析

    • 函数适配器(很重要)

    第一个问题:理解typedef含义

    • struct unary_function

    定义一个类,然后 里面有几个个成员,没有看出里面的有什么作用呀?

    提前定义好成员变量,这样才能实现类型多态的调用

    binary_function is a base class for creating function objects with two arguments.

    // 一元函数的参数类型和返回值类型
    template <class _Arg, class _Result>
    //stl_function.h::38
    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;
      typedef _Arg2 second_argument_type;
      typedef _Result result_type;
    };  
    
    std::unary_function<int,bool>
    
    

    ### 应用于仿函数,function adapter

    > 对返回值进行逻辑否定:not1, not2

    > 对参数进行绑定:bind1st, bind2nd

    > 用于函数合成:compose1, compose2

    > 用于函数指针:ptr_fun

    > 用于成员函数指针:mem_fun, mem_fun_ref

    成员类型 定义 注释
    argument_type 第一个模板参数 (Arg) ()重载函数的参数类型
    result_type 第二个模板参数(Result) ()重载函数的返回值类型
    成员类型 定义 注释
    first_argument_type 第一个模板参数(Arg1) ()重载函数的第一个参数类型
    second_argument_type 第一个模板参数 (Arg2) ()重载函数的第二个参数类型
    return_type 第一个模板参数(Result) ()重载函数的返回值类型
    • class binder2nd
    //第一次分析:class binder2nd 声明一个类 这个语法你肯定明白
    //第二次分析:class binder2nd:public unary_function 
    //binder2nd继承来了模板类unary_function,typename _Operation::first_argument_type 是参数类型 
    //vector<int> int就是类型  vector就是模板类  这个对比应该明白  
    //第三次分析:  _<typename _Operation::first_argument_type
    //请问 class  _Operation是任意类, typename first_argument_type任意类型之间关系是什么?
    //但是在stl语法中 typename T 代码是 成员变量的类型(int ,char*)   class T 代表类的类型
    //vector:base, a.m_i 
    //T::T
    //第四次分析:
    // _Operation::second_argument_type 你怎么确定  类_Operation里面一定有成员变量 second_argument_type
    // 
    //binary_function is a base class for creating function objects with two arguments.
    //stl 规定 函数对象必须这个类, 这样函数对象之间(虽然不是继承,但是可以调用),但是相互使用了(佩服呀,因此函数对象适配器,可以适配任何同类对象)
    // 这就是编译期间的多态
    
    ////第五次分析:重载 返回值 operator()(参数)
    //typename _Operation::result_type operator()(const typename _Operation::first_argument_type& __x) const 
     //返回的结果是不是具体类型 是模板  result_type是unary_function实现的
     //op(__x, value) 
    
    ////第6次分析:
    //binder2nd::unary_function
    //_Operation::binary_function
    //这是一次更强大的适配
    //这个是第六次分析   关键点 
    //value 是 binder2nd是创建时候调用构造时候设置的,
    //_x 调用函数关系()设置的
    //A a(10)  构造(a) 
    // a(20)   函数调用(a,b)
    /**
    此函数适配器必须要继承自unary_function对象,满足可配接性。
    解释一下可配接性。less_equal类继承自binary_function,便有了内部嵌套类型second_argument_type,
    而这个类型正好需要用在binder2nd中,以保存(绑定)某个参数。这样,less_equal就变为了可配接的。
    纵观整个适配器系统,基本上都是把某个对象或指向对象的指针封装在一个适配器类中,对适配器的操作最终都会传递到对所包含对象的操作
    **/
    
    template <class _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) // 绑定的第二个数
      {
    
      }
      typename _Operation::result_type //返回的结果是不是具体类型 是模板  result_type是unary_function实现的
    
     
      typename _Operation::result_type operator()(const typename _Operation::first_argument_type& __x) const 
      {
         return op(__x, value);  
         //这个是第六次分析   关键点 
         //value 是 binder2nd是创建时候调用构造时候设置的,
         //_x 调用函数关系()设置的
         //A a(10)  构造(a) 
         // a(20)   函数调用(a,b)
        
      }
    };
    
    • 测试程序;
    std::vector<int> vec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    //计算容器中小于等于3的元素个数
    cout << count_if(vec.begin(), vec.end(), bind2nd(less_equal<int>(), 3));
    int count  = std::count_if(vec.begin(), vec.end(), [](int x) {return x >  3;})
    
    1548330412420.png

    lambda表达式的本质(函数对象)

    lambda表达式就是一个函数对象

    当编写了一个lambda表达式的时候,编译器将该表达式翻译成一个未命名类的未命名对象

    int num = 100;
    auto f = [num](){return num; };//等价于F
    
    class F
    {
    public:
        F(int n) :num(n){}
        int operator()() const { return num; }
    private:
        int num;
    };
    

    相关代码:

    相关文章

      网友评论

          本文标题:小王职场记STL(3)lambda表达式的本质

          本文链接:https://www.haomeiwen.com/subject/copksqtx.html