• C++11闭包函数的几种实现方法


    什么是闭包函数

    函数就是对传入的一组参数进行运算的行为,闭包函数就是有状态的函数,在参与运算时,除了传入的参数外,还可以对上下文的状态进行运算。类函数运行时就是典型的闭包函数,类函数运行起来后其对象就是状态上下文,函数内部可以使用类对象的数据。

    闭包函数的几种实现方式

    闭包函数可以采用仿函数、函数绑定和Lambda表达式来实现。

    仿函数

    仿函数就是将类函数化,即实现类的()运算符,()运算符可以带参数,也可以不带参数。
    举例如下:

    #include 
    #include 
    using namespace std;
    
    class MyFunctor
    {
    public:
        MyFunctor(int i) : r(i){}//构造函数
    
        //仿函数,重载operator()
        int operator() (int tmp)
        {
            return tmp+r;
        }
    private:
        int r;
    };
    
    int main()
    {
        MyFunctor obj(10);
        //调用仿函数
        cout << "result=" << obj(1) << endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    执行结果为:
    result=11

    std::bind实现闭包函数

    在介绍std::bind之前,先介绍function模板。

    function模板

    function模板相当于函数指针,它可以指向全局函数、类中静态函数、仿函数。

    #include 
    #include 
    #include 
    
    using namespace std;
    
    //1.普通函数
    void func()
    {
        cout << __func__ <<endl;
    }
    
    //2.类中静态函数
    class Test
    {
      public:
        static int test_func(int a)
        {
            cout << __func__ << "(" << a << ") ->:";
            return a;
        }
    
    };
    
    //3.类中仿函数
    class MyFunctor
    {
    public:
        //仿函数,重载operator()
        int operator() (int a)
        {
            cout << __func__ << "(" << a << ") ->:";
            return a;
        }
    };
    
    int main()
    {
        //1.绑定普通函数
        function<void(void)> f1=func;
        f1();
    
        //2.绑定类中静态函数
        function<int(int)> f2=Test::test_func;
        cout << f2(10) << endl;
    
        //3.绑定类中的仿函数
        MyFunctor obj;
        function<int(int)>  f3=obj;
        cout << f3(33) << endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    执行结果如下:
    func
    test_func(10) ->:10
    operator()(33) ->:33

    std::bind绑定全局函数

    std::bind可以通过传入的n个参数得到函数对象,然后执行函数。在得到函数对象前,可以明确传递参数;也可以先传递占位符,待执行函数的时候再替换占位符。
    举例如下:

    #include 
    #include 
    using namespace std;
    
    //1.普通函数
    void func(int x, int y)
    {
        cout << x << ", " << y << endl;
    }
    
    int main()
    {
        bind(func, 11, 22)();
        bind(func, std::placeholders::_1, std::placeholders:: _2)(22,11);   //22对应前面的_1,11对应前面的_2
        bind(func, std::placeholders::_1, 22)(22,11, 33,44);    //后面的11,33,44没有作用
        bind(func, std::placeholders::_3, 22)(22,11, 33,44);    //后面的11,33,44没有作用
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    执行结果如下:
    11, 22
    22, 11
    22, 22
    33, 22

    std::bind绑定成员函数

    直接上代码:

    #include 
    #include    //std::bind
    using namespace std;
    using namespace std::placeholders;
    
    class Test
    {
    public:
        void func(int x, int y)
        {
            //成员函数
            cout << x << "," << y << endl;
        }
    
        int a;  //成员变量
    };
    
    int main()
    {
        Test obj;
        //绑定成员函数
        function<void(int,int)> f1=bind(&Test::func, &obj, _1, _2);
        f1(11, 22);
    
        //绑定成员变量
        function<int &()> f2=bind(&Test::a, &obj);
        f2()=111;   //obj.a=111
        cout<<"obj.a="<<obj.a<<endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    执行结果为:
    11,22
    obj.a=111

    Lambda表达式实现闭包函数

    Lambda表达式介绍

    lambda 表达式(lambda expression)是一个匿名函数,lambda表达式基于数学中的 λ 演算得名。

    C++11中的lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。

    lambda表达式的基本构成:
    在这里插入图片描述
    ① 函数对象参数
    [],标识一个lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义lambda为止时lambda所在作用范围内可见的局部变量(包括lambda所在类的this)。函数对象参数有以下形式:
     空。没有使用任何函数对象参数。
     =。函数体内可以使用lambda所在作用范围内所有可见的局部变量(包括lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
     &。函数体内可以使用lambda所在作用范围内所有可见的局部变量(包括lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
     this。函数体内可以使用lambda所在类中的成员变量。
     a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
     &a。将a按引用进行传递。
     a, &b。将a按值进行传递,b按引用进行传递。
     =,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。
     &, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。
    ② 操作符重载函数参数
    标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。
    ③ 可修改标示符
    mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
    ④ 错误抛出标示符
    exception声明,这部分也可以省略。exception声明用于指定函数抛出的异常,如抛出整数类型的异常,可以使用throw(int)
    ⑤ 函数返回值
    ->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
    ⑥ 是函数体
    {},标识函数的实现,这部分不能省略,但函数体可以为空。

    class Test
    {
    public:
        int i = 0;
    
        void func(int x, int y)
        {
            auto x1 = []{ return i; };          //err, 没有捕获外部变量
            auto x2 = [=]{ return i+x+y; };     //ok, 值传递方式捕获所有外部变量
            auto x3 = [&]{ return i+x+y; };     //ok, 引用传递方式捕获所有外部变量
            auto x4 = [this]{ return i; };      //ok, 捕获this指针
            auto x5 = [this]{ return i+x+y; };  //err, 没有捕获x, y
            auto x6 = [this, x, y]{ return i+x+y; };//ok, 捕获this指针, x, y
            auto x9 = [this]{ return i++; };        //ok, 捕获this指针, 并修改成员的值
        }
    };
    
    int main()
    {
        int a = 0, b = 1;
        auto f1 = []{ return a; };      //err, 没有捕获外部变量
        auto f2 = [=]{ return a; };     //ok, 值传递方式捕获所有外部变量
        auto f3 = [=]{ return a++; };   //err, a是以赋值方式捕获的,无法修改
        auto f4 = [=]() mutable { return a++; };   //ok, 加上mutable修饰符后,可以修改按值传递进来的拷贝
        auto f5 = [&]{ return a++; };               //ok, 引用传递方式捕获所有外部变量, 并对a执行自加运算
        auto f6 = [a]{ return a+b; };               //err, 没有捕获变量b
        auto f9 = [a,&b]{ return a+(b++); };        //ok, 捕获a, &b
        auto f8 = [=,&b]{ return a+(b++); };        //ok, 捕获所有外部变量,&b
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    值传递和引用传递区别:

    int main()
    {
        int j = 12;
        auto by_val_lambda = [=] { return j + 1;};
        auto by_ref_lambda = [&] { return j + 1;};
        cout << "by_val_lambda: " << by_val_lambda() << endl;
        cout << "by_ref_lambda: " << by_ref_lambda() << endl;
    
        j++;
        cout << "by_val_lambda: " << by_val_lambda() << endl;
        cout << "by_ref_lambda: " << by_ref_lambda() << endl;
    
        /*
        运行结果:
            by_val_lambda: 13
            by_ref_lambda: 13
            by_val_lambda: 13
            by_ref_lambda: 14
        */
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    第3次调用结果还是13,原因是由于by_val_lambda中,j被视为了一个常量,一旦初始化后不会再改变。

  • 相关阅读:
    Web3 游戏周报(6.23 - 6.29)
    WebStorm使用PlantUML
    logback+MQ+Logstash 日志收集
    Debezium日常分享系列之:使用数据库中的数据流进行在线机器学习
    EPOLL(C/S模型)实现I/O复用多进程聊天室,通过共享内存、socketpair实现父子进程通信,通过信号量回收进程
    第二十八章 车道线检测中的论文梳理(车道线感知)
    Vue中的数据代理与数据劫持
    Halcon WPF 开发学习笔记(0):开篇介绍
    Elasticsearch(macbook搭建,Elasticsearch+kibana)一步到位
    【C++】---了解string类
  • 原文地址:https://blog.csdn.net/xiongpursuit88/article/details/128172549