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绑定参数都可以实现“闭包”。

原文地址:https://www.cnblogs.com/milanleon/p/7562793.html