C++11新特性:lambda表达式

C++11新特性:lambda表达式

概念

lambda 表达式定义了一个匿名函数,并且可以捕获一定范围内的变量。

基本语法

[捕获列表] (参数列表) 选项 -> 返回值 { lambda函数体 };
  • 捕获列表【必须】:捕获列表是lambda表达式的标识,lambda表达式通过捕获列表获取外部作用域范围内的变量。
  • 参数列表【可选】:lambda函数体中除了从外部捕获的变量,其他需要从外界传入的参数(跟仿函数一样)
  • 选项【可选】:仅有一个mutable,用于在使用=捕获时仍能修改捕获变量的值
  • 返回值【可选】:lambda函数的返回值(编译器能自动推导,但是对于初始化列表{x1,x2,...}这种需要手动指明具体的返回值类型)

一个完整的lambda表达式示例:

int a = 2;
auto fun = [=](int b) -> int { return a + b + 1; };
std::cout << fun(3) << std::endl;  // 输出: 6

捕获列表

捕获的范围是上一级作用域。

  • [] 不捕获任何变量。
  • [&] 捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。
  • [=] 捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。
  • [=, &foo] 按值捕获外部作用域中所有变量,并按引用捕获 foo 变量。
  • [&, foo] 按引用捕获外部作用域中所有变量,并按值捕获 foo 变量。
  • [bar] 按值捕获 bar 变量,同时不捕获其他变量。
  • [this] 捕获当前类中的 this 指针,让 lambda 表达式拥有和当前类成员函数同样的访问权限。如果已经使用了 & 或者 =,就默认添加此选项。捕获 this 的目的是可以在 lamda 中使用当前类的成员函数和成员变量。

注意

  • 按值捕获无法修改捕获到的变量的值,除非加上 mutable 选项,或者干脆按引用捕获。

深入

lambda 表达式可以说是就地定义仿函数闭包的“语法糖”。

编译器视角下的lambda表达式

编译器会把我们写的lambda表达式翻译成一个类,并在类中重载 operator()

示例

(1)按值捕获

写了一个lambda表达式:

int x = 1; int y = 2;
auto plus = [=] (int a, int b) -> int { return x + y + a + b; };
int c = plus(1, 2);

那么编译器的翻译结果:

class LambdaClass {
public:
    LambdaClass(int xx, int yy)
    : x(xx), y(yy) {}

    int operator () (int a, int b) const {
        return x + y + a + b;
    }

private:
    const int x;
    const int y;
};

int x = 1; int y = 2;
LambdaClass plus(x, y);
int c = plus(1, 2);

(2)按引用捕获

写了一个lambda表达式:

int x = 1; int y = 2;
auto plus = [&] (int a, int b) -> int { x++; return x + y + a + b; };
int c = plus(1, 2);

那么编译器的翻译结果:

class LambdaClass {
public:
    LambdaClass(int& xx, int& yy)
    : x(xx), y(yy) {}

    int operator () (int a, int b) {
        x++;
        return x + y + a + b;
    }

private:
    int &x;
    int &y;
};

int x = 1; int y = 2;
LambdaClass plus(x, y);
int c = plus(1, 2);

于是可以发现:捕获列表捕获住的任何外部变量,最终均会变为闭包类型的成员变量。

  • 按值捕获
    • 按值捕获的变量均以const修饰
    • 捕获到的值以值传递的方式初始化为成员变量的值。
    • 重载的operator()函数是const修饰的常函数,不能修改成员变量的值,因此加上mutable选项后就能修改按值捕获的变量的值了。
  • 按引用捕获
    • 捕获到的引用以引用传递的方式初始化为成员变量的值(建立引用)
    • 重载的operator()函数没有const修饰

针对上面的集中情况,lambda的各个成分和类的各个成分对应起来如下:

捕获列表,对应LambdaClass类的private成员。其中按值捕获的变量修饰为const成员变量,按引用捕获的就是普通的成员变量

参数列表,对应LambdaClass类的成员函数的operator()的形参列表

mutable,对应 LambdaClass类成员函数 operator() 的const属性 ,但是只有在捕获列表捕获的参数不含有引用捕获的情况下才会生效,因为捕获列表只要包含引用捕获,那operator()函数就一定是非const函数

返回类型,对应 LambdaClass类成员函数 operator() 的返回类型

**函数体,**对应 LambdaClass类成员函数 operator() 的函数体。

引用捕获和值捕获不同的一点就是,对应的成员是否为引用类型。

参考:

C++ Lambda表达式的完整介绍 - 知乎 (zhihu.com)

parallel processing - lambda expression error: expression must be a modifiable lvalue - Stack Overflow

你可能感兴趣的:(C++,c++,算法,开发语言)