C++-Lambda表达式

简介

对于只使用一次的函数对象类,能否直接在使用它的地方定义呢?Lambda 表达式能够解决这个问题。使用 Lambda 表达式可以减少程序中函数对象类的数量,使得程序更加优雅。

定义

1
2
3
4
[外部变量访问方式说明符] (参数表) -> 返回值类型
{
语句块
}

外部变量访问方式说明符

  • 没有任何函数对象参数。
  • = 函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是值传递方式(相
    当于编译器自动为我们按值传递了所有局部变量)。
  • & 函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是引用传递方式
    (相当于是编译器自动为我们按引用传递了所有局部变量)。
  • this 函数体内可以使用 Lambda 所在类中的成员变量。
  • a将 a 按值进行传递。按值进行传递时,函数体内不能修改传递进来的 a 的拷贝,因为默认情况下函数是 const 的,要
    修改传递进来的拷贝,可以添加 mutable 修饰符。
  • &a 将 a 按引用进行传递。
  • a,&b 将 a 按值传递,b 按引用进行传递。
  • =,&a,&b 除 a 和 b 按引用进行传递外,其他参数都按值进行传递。
  • &,a,b除 a 和 b 按值进行传递外,其他参数都按引用进行传递。

实例

下面是一个合法的Lambda表达式:

1
[=] (int x, int y) -> bool {return x % 10 < y % 10; }

Lambda 表达式实际上是一个函数,只是它没有名字。下面的程序段使用了上面的 Lambda 表达式:

1
2
3
4
int a[4] = {11, 2, 33, 4};
sort(a, a+4, [=](int x, int y) -> bool { return x%10 < y%10; } );
for_each(a, a+4, [=](int x) { cout << x << " ";} );
//输出为:11 2 33 4

程序第 2 行使得数组 a 按个位数从小到大排序。具体的原理是:sort 在执行过程中,需要判断两个元素 x、y 的大小时,会以 x、y 作为参数,调用 Lambda 表达式所代表的函数,并根据返回值来判断 x、y 的大小。这样,就不用专门编写一个函数对象类了。

第 3 行,for_each 的第 3 个参数是一个 Lambda 表达式。for_each 执行过程中会依次以每个元素作为参数调用它,因此每个元素都被输出。

下面是用到了外部变量的Lambda表达式的程序:

1
2
3
4
5
6
7
int a[4] = { 1, 2, 3, 4 };
int total = 0;
for_each(a, a + 4, [&](int & x) { total += x; x *= 2; });
cout << total << endl; //输出 10
for_each(a, a + 4, [=](int x) { cout << x << " "; });
//输出为:10
// 2 4 6 8

[&]表示该 Lambda 表达式中用到的外部变量 total是传引用的,其值可以在表达式执行过程中被改变(如果使用[=],编译无法通过)。该 Lambda 表达式每次被 for_each 执行时,都将 a 中的一个元素累加到 total 上,然后将该元素加倍。

实际上,“外部变量访问方式说明符”还可以有更加复杂和灵活的用法。例如:

  • [=, &x, &y]表示外部变量 x、y 的值可以被修改,其余外部变量不能被修改;

  • [&, x, y]表示除 x、y 以外的外部变量,值都可以被修改。

Lambda实现原理

通过反汇编可以查看到Lambda的调用为:

1
2
3
...
0117531F call <Lambda_xxxxxx>::operator()(xxxx)
...

即编译器底层为Lambda函数创建了匿名类的匿名对象并调用了operator()。详解可见C++-Lambda实现原理