zoukankan      html  css  js  c++  java
  • C++11--lambda表达式

    1、综述

      lambda表达式是一个匿名函数,它可以在函数内部定义,编译器会将lambda表达式当做一个函数对象。lambda表达式的形式为:[捕获列表] (参数列表) -> 返回类型{ 函数体 },其中“捕获列表”是表达式所在函数中定义的局部变量的列表,可以为空,而且lambda必须使用尾置返回来指定函数返回类型,eg:

    int main()
    {
        function<int(int)> func = [](int x)->int { return x * x; };
        cout << func(5) << endl;
    
        auto Func = [](int x)->int { return x * x; };
        cout << Func(10) << endl;
    
        int(*FuncPtr)(int) = Func;
        cout << (*FuncPtr)(20) << endl;
    
        return 0;
    }
    View Code

       lambda表达式的函数返回类型也可以省略,这时候lambda根据函数体中return语句自动推断出返回类型,如果函数体中没有return语句的话则会认为返回类型为void,代码示例如下。有些情况下,lambda不能自动推断出返回类型,比如函数体内含有多个return语句(if(x) return i; else return -1;),这个时候编译会出错,所以最保险的是不省略返回类型。

        function<int(int)> func = [](int x)/*->int*/{ return x * x; };
    
        auto Func = [](int x){ return x * x; };
    View Code

      如果lambda表达式的没有参数的话还可以忽略参数列表,eg 

        function<int()> func = [] { return 100; };
        cout << func() << endl;
    
        auto Func = [] { return 100; };
        cout << Func() << endl;
    
        return 0;
    View Code

       “捕获列表”是lambda表达式所在函数中定义的局部变量的列表,它用来指出在表达式内部可以使用这些变量。“捕获列表”中的变量只能是局部变量,且不能static类型,对于static类型的局部变量,表达式内部可以直接使用,而且static类型变量是引用捕获。前面说过编译器会将lambda表达式当做一个函数对象,所以“捕获列表”中的变量相当于是将其保存在了函数对象中。eg:

    int main()
    {
        int i = 10;
        int j = 10;
        static int k = 0;
        auto Func = [i, j](int x)
        {
            k;
            return x * i * j;
        };
        cout << Func(5) << endl; //输出500
    
        return 0;
    }
    View Code

        lambda表达式可以用在STL算法中,如下代码展示了sort()算法分别使用函数指针,函数对象,lambda表达式的示例:  

    bool myFunction(const int& i, const int& j) 
    { 
        return i > j; 
    }
    
    class myClass
    {
    public:
        bool operator() (const int& i, const int& j) { return i > j; }
    };
    
    int main()
    {
        vector<int> v = list_of(5) (2) (4) (3) (1) (5);
        sort(v.begin(), v.end(), myFunction);
        sort(v.begin(), v.end(), myClass());
        sort(v.begin(), v.end(), [](const int& i, const int& j){ return i > j; });
    
        for (auto iter = v.begin(); iter != v.end(); iter++)
            cout << *iter << endl;
    
        return 0;
    }
    View Code

    2、再谈变量的捕获

      “捕获列表”中的变量的捕获也可以是值捕获或引用捕获,如果是值捕获的话,传给捕获列表的变量在Lambda中实际上为变量的一个副本,而且被捕获的变量的值是在lambda表达式创建的时候就拷贝,而不是像函数参数的值传递那样在调用时拷贝,而且采用值捕获的变量如果会在函数体内修改的话还要使用mutable关键字来声明方法,eg:

    int main()
    {
        int n = 10;
        auto func = [n]()mutable{ return ++n;}; //lambda函数体内会修改n,所以应该加mutable关键字
        n = 0;
        auto num = func(); //n是lambda创建的时候拷贝而不是调用的时候拷贝,所以num是11而不是0
    
        return 0;
    }
    View Code

      如果是引用捕获的话在“捕获列表”中的捕获变量需要加&,eg:

    int main()
    {
        int n = 10;
        auto func = [&n](){ return ++n;}; //n是引用捕获
        n = 0;
        auto num = func(); //lambda调用的时候n为0,执行完lambda函数体后,num值为1,n为1
    
        return 0;
    }
    View Code

       我们也可以不传递“捕获列表”,让编译器根据lambda体中的变量使用代码来推断出哪些变量是捕获变量,即隐式捕获。为了指示由编译器推断捕获变量,应该在捕获列表中写一个=或&,=表示使用值引用方式,&表示采用引用捕获方式。如果我们希望对一部分变量采用值捕获,另一部分变量采用引用捕获,还可以混合使用隐式捕获和显示捕获。eg:

    int main()
    {
        int n = 10;
        auto func1 = [=]()mutable{ return ++n;}; //n是隐式值捕获
        auto func2 = [&]() {return ++n;}; //n是隐式引用捕获
        int i = 1, j = 2;
        auto func3 = [=, &i, &j]() {return n * i * j;}; //n是隐式值捕获,i和j是显示引用捕获
        auto func4 = [&, i, j]() {return n * i * j;}; //n是隐式引用捕获,i和j是显示值捕获
    
        return 0;
    }
    View Code

     可以在捕获列表中传入当前类的指针this,这样就可以在lamda中直接使用当前类的成员函数和成员变量:

    class Foo
    {
    public:
        Foo()
        {
            Bar* b = new Bar;
            b->onClick = [this]{
                cout << "onClick" << endl;
                func();
            };
        }
        void func(){}
    };
    
    class Bar
    {
    public:
        std::function<void()> onClick;
    };
    View Code

     捕获总结:

      1)、[]不捕获任何变量。
      2)、[bar]按值捕获bar变量。
           3)、[&foo]按引用捕获foo变量。
      4)、[=]捕获外部作用域中所有变量,并作为副本在函数体中使用,即按值捕获。
      5)、[&]捕获外部作用域中所有变量,并作为引用在函数体中使用,即按引用捕获。
      6)、[=,&foo]按值捕获外部作用域中所有变量,并按引用捕获foo变量。
      7)、[this]捕获当前类中的this指针,让lambda表达式拥有和当前类成员函数同样的访问权限,如果已经使用了&或者=,就默认添加此选项。

    3、lambda与函数

      对于那些只在一两个地方使用的简单操作,可以直接使用lambda表达式。如果一个操作会被多个地方会使用,或者操作很复杂(包含多条语句),最好还是定义一个函数来使用。

      lambda表达式捕获变量的功能与bind绑定参数有相似的效果。

      对于子函数可以使用父函数中的局部变量这种行为,我们可以称做“闭包”,即“闭包”就是能够读取其他函数(父函数)中内部变量的函数(子函数),通常就是定义在一个函数内部的函数。可见lambda表达式和bind绑定参数都可以实现“闭包”。

  • 相关阅读:
    Java中Iterator类的详细介绍
    Java中Iterator类的详细介绍
    Java中Iterator类的详细介绍
    Java中Iterator类的详细介绍
    VC++的Unicode编程
    实现系统滚动条换肤功能
    透明位图的显示
    用于树控件消息处理的几个结构
    C++实现半透明按钮控件(PNG,GDI+)
    Bootstrap+Knockout.JS+ASP.Net MVC3+PetaPOCO实现CRUD操作
  • 原文地址:https://www.cnblogs.com/milanleon/p/7562793.html
Copyright © 2011-2022 走看看