C++11:function函数对象、bind绑定器、从cppinsights.io看lambda表达式原理

function函数对象、bind绑定器和lambda表达式

前置知识

  • C++中,将所有能当函数使用的对象都称为函数对象;

  • 函数类型:函数的类型由返回值和形参一起决定

    //函数
    bool Compare(const string&,const string&);
    //函数类型
    bool(const string&,const string&)
    
  • 函数指针:

    • 指向一个函数类型所表示函数的指针,无需解引用可以和函数使用一样正常调用:

      // 声明一个函数指针,指向返回值是bool类型,
      // 形参列表是(const string&,const string&)的函数
      bool (*pf)(const string&,const string&);
      // 正常赋值
      pf = Compare;    //将pf指向Compare函数
      pf = &Compare; // 等价于上面
      // 函数指针的使用,下面三者等价
      bool b1 = pf("hello","good");
      bool b2 = (*pf)("hello","good");
      bool b3 = Compare("hello","good");
      
    • 指向不同函数类型的指针之间没有转换规则,函数指针赋值过程中,函数类型必须匹配

    • 将函数名做参数传递给其他函数,函数名会转变成为函数指针,所以通过函数指针接收即可

  • 仿函数/函数对象:通过函数类调用operator()重载,实现和函数一样的使用

  • 函数对象和函数指针:

    • 通过函数指针间接调用函数是无法内联的,导致函数调用开销,效率较低
    • 使用函数对象,就可以进行内联

一、bind绑定器

1、前置

  • bind1stbind2nd:将二元函数对象变成一元函数对象
    • bind1st :将函数对象的operator()的第一个参数绑定为一个确定的值,返回一个新得函数对象

    • bind2nd:将函数对象的operator()的第二个参数绑定为一个确定的值,返回一个新得函数对象

    • 局限性: 只能用于二元函数对象,不能用于普通函数,但是可以将普通函数通过std::ptr_fun()转化为函数对象(仿函数)

      #include <iostream>
      #include <functional>
      using namespace std;
      void foo(int a,int b) {
          std::cout << "Sum: " << a+b  << std::endl;
      }
      
      int main() {
          auto bindFunc = std::bind1st(ptr_fun(foo),10);
          auto bindFunc2 = std::bind(foo, 10, placeholders::_1);
          bindFunc(20);  // 调用 foo(10, 20)
          bindFunc2(2);  // 调用 foo(10,2)
          return 0;
      }
      

2、C++11 bind绑定器

  • 解决了bind1stbind2nd的二元函数的局限性,可以用于多元函数对象,最多可以有20个绑定参数,并且还可以绑定普通函数(退化为指针)函数指针成员函数指针等,返回一个新的函数对象,新的函数对象的具体类型取决于被绑定的参数

  • bind绑定器占位符命名空间placeholders:表示待输入的参数,可以调整传入的顺序

    namespace placeholders {
        _INLINE_VAR constexpr _Ph<1> _1{}; //表示第一个参数
        _INLINE_VAR constexpr _Ph<2> _2{}; //表示第二个参数
        _INLINE_VAR constexpr _Ph<3> _3{};
        _INLINE_VAR constexpr _Ph<4> _4{};
        ....
        _INLINE_VAR constexpr _Ph<20> _20{};
    }
    
  • bind的基本使用:可以帮定普通函数也可以是成员函数

    #include <iostream>
    #include <functional>
    using namespace std;
    // 普通函数
    	int add(int x, int y)
    {
        return x + y;
    }
    // 成员函数
    class ADD
    {
    public:
        int add(int x, int y)
        {
            return x + y;
        }
    };
    int main()
    {
        ADD obj;
        //绑定普通函数返回函数对象
        auto f1 = bind(add, placeholders::_1, 2); 
        //绑定成员函数返回函数对象
        auto f2 = bind(&ADD::add, &obj,2, placeholders::_1);
        cout << f1(1) << endl;
        cout << f2(1) << endl;
        return 0;
    }
    
    

二、function函数对象

  • function是个模板类,可以对函数类型进行推演,底层调用实现的也是operator()()重载,不过调用的是封装的可调用实体目标,些目标实体包括:普通函数lambda表达式函数指针函数对象类的成员函数以及类的静态成员函数

  • bind返回的函数对象 和 lambda匿名函数,一般只能作用一条语句中,function可以用来保留他们的类型,在多条语句中使用

  • 基本使用

    #include<iostream>
    #include<functional>
    #include<string>
    using namespace std;
    void hello1() // 函数类型:void(),返回值是void,不带参数
    {
        cout << "hello word" << endl;
    }
    
    class Test
    {
    public:
    	//成员函数的函数指针,void(Test::*pfunc)(string)
        void hello(string str) { cout << str << endl; }
    };
    int main()
    { 
     // 通过function函数对象封装 普通函数hello1
      function<void()> func1 = hello1;
      func1();  // 等价于func2.operator() => hello1()
      
      // 封装 函数对象(lambda表达式本质就是函数对象)
      function<int(int,int) fun2 = [](int a,int b)->int{return a+b;};
      cout<< func2(100,200)<<endl;
      
      // 封装 类成员函数
      function<void(Test&&,string)> fun3 = &Test::hello;
      func3(Test(),"hello");
     return 0;
    }
    
  • function底层原理:

    void hello(string str){cout << str << endl;}
    int sumA(int a, int b) { return a + b; }
    
    //typename... A   可变参,表示A不是一个类型而是一组类型
    template<typename R, typename... A>  
    //表示部分特例化 ,对有一个返回值,形参个数不全的函数进行特例化
    class myfunction<R(A...)>  
    {
    public:
        using PFUNC = R(*)(A...); //定义函数指针
        myfunction(PFUNC pfunc) :_pfunc(pfunc) {} //给函数指针赋值
        //arg 表示的是一组参数   A...可变参类型, 可以表示很多类型
        R operator()(A... arg)  
        {
            return _pfunc(arg...);  
        }
    private:
        PFUNC _pfunc;
    };
    
    
    int main()
    {
        myfunction<void(string)> func1(hello);
        //helloworld!  => func1.operator("helloworld!");
        func1("helloworld!");  
    
        myfunction<int(int,int)> func2(sumA); 
        cout << func2(20, 20) << endl; //40
        return 0;
    }
    

三、lambda表达式

  • lambda表达式的底层实现就是函数对象,创建的是匿名函数对象,相比于函数对象(必须定义一个类)更加灵活

  • 函数对象:

    class add
    {
    public:
    add(){} //构造函数,用于捕获外部变量
    /* (int a,int b) 形参列表, int返回值类型 */
    int operator()(int a,int b){/*函数体*/} 
    };
    
  • lambda表达式的基本语法:

    • [捕获外部变量](形参列表)->返回值 {函数体};
    • ->返回值类型
      • 相当于函数对象operator()()重载的返回值类型
      • 没有返回值,也就是返回值是void,则 ->返回值类型 可以省略
    • (形参列表)
      • 相当于函数对象operator()()重载的形参列表
    • {函数体}
      • 相当于函数对象operator()()重载中的函数体
    • [捕获外部变量]
      • 相当于是函数对象的构造函数
      • [] => 不捕获外部变量,匿名函数没有成员变量
      • [=]=>以值传递的方式捕获外部所有变量
      • [&]=>以引用的方式捕获外部所有变量
      • [this] => 捕获外部this指针,用于类中使用
      • [=,&a] =>以值传递的方式捕获外部所有变量,对变量a采用引用的方式捕获
      • [a,b] => 以值传递的方式捕获外部变量a,b
      • [a,&b]=>以值传递的方式捕获外部变量a,以引用的方式捕获变量b
  • lambda匿名函数的底层实现(简版)

    • 前置代码

      	#include<iostream>
      	using namespace std;
      	int main()
      	{
      	 int a=10;
      	 int b=20;
      	 // 裸lambda
      	 auto fu = [](){};
      	 // 带返回值
      	 auto fun = []()->int{};
      	 //带参数列表、返回值、函数体
      	 auto func = [](int a,int b)->int{return a+b;};
      	 //值传递外部捕获、参数列表、返回值、函数体
      	 auto func1 = [=](int c,int d)->int{int f =a;return c+d;};
      	 //值传递、mutable、函数体
       	 auto func2 = [=]()mutable{int tmp = a;a=b;b=tmp ;}; 
       	 //引用传递、函数体
      	 auto func3 = [&]() {int tmp = a; a = b; b = tmp; };
      	 return 0;
      	}
      
    • 什么都没有的裸匿名函数对象:

      • auto fu = [](){};

      • 底层实现:

        class __lambda_8_12
         {
         public: 
         // 返回值位空,没有函数体和形参列表
            inline void operator()() const{}
         };
         _lambda_8_12 fu = __lambda_8_12(__lambda_8_12{});
        
    • 只带返回值的匿名函数对象:

      • auto fun = []()->int{};

      • 底层实现:

        class __lambda_9_13
         {
         public: 
         //带返回值
            inline int operator()() const{} //只读属性
         };
         __lambda_9_13 fun = __lambda_9_13(__lambda_9_13{});
        
    • 形参列表返回值函数体的匿名函数对象:

      • auto func = [](int a,int b)->int{return a+b;};

      • 底层实现

          class __lambda_10_14
          {
            public: 
            inline int operator()(int a, int b) const //只读属性
            {
              return a + b;
            }
          };
          __lambda_10_14 func = __lambda_10_14(__lambda_10_14{});
        
    • 值传递形参列表返回值函数体的匿名函数对象:

      • auto func1 = [=](int c,int d)->int{int f =a;return c+d;};

      • 底层实现:

        class __lambda_11_15
          {
            public: 
            inline int operator()(int c, int d) const //只读属性
            {
              int f = a;
              return c + d;
            }
            private: 
            int a;
            __lambda_11_15(int & _a): a{_a} {} //以传传递捕获外部变量
          };
           __lambda_11_15 func1 = __lambda_11_15(__lambda_11_15{a});
        
    • 值传递形参列表函数体mutable关键字的匿名函数对象:

      • auto func2 = [=]()mutable{int tmp = a;a=b;b=tmp ;};

      • 底层实现:

         class __lambda_12_15
          {
            public: 
            inline void operator()() //可读可写
            {
              int tmp = a;
              a = b;
              b = tmp;
            }
            private: 
            int a;
            int b;
            public: 
            __lambda_12_15(int & _a, int & _b): a{_a}, b{_b} {}
          };
          __lambda_12_15 func2 = __lambda_12_15(__lambda_12_15{a, b});
        
    • 引用传递函数体的匿名函数对象:

      • auto func3 = [&]() {int tmp = a; a = b; b = tmp; };

      • 底层实现:

          class __lambda_13_15
          {
            public: 
            inline void operator()() const //只读属性
            {
              int tmp = a;
              a = b;
              b = tmp;
            }
            private: 
            int & a; //引用接收外部变量
            int & b;
            public: 
            __lambda_13_15(int & _a, int & _b): a{_a}, b{_b}{}
          };
          __lambda_13_15 func3 = __lambda_13_15(__lambda_13_15{a, b});
        
    • TIP:

      • 不带mutable关键的lambda匿名函数对象中的operator()()重载是只读属性的(const),不能修改从外部捕获的成员变量
      • 只要选择外部引入方式,且在函数体内部使用了这个变量,才会生成对应的成员变量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值