Lamada 表达式是 C++11 最重要也是最常用的特性之一。Lamada 来源于函数式编程的概念,也是现代编程语言的一个特点。

一、Lamada 表达式定义

下图展示出了 C++ Lamada 表示的组成部分:

其中:
① 指明捕获列表。
② 指明参数列表。
mutable可选项。和常规的 mutable 用法类似,即当 lamada 表达式参数是 const 时,使用mutable可以取消这种 const。
throw可选项。可以使用noexcept指明/约束表达式内不会抛出异常。
⑤ 指定返回类型。
⑥ Lamada 函数体。

根据上图定义一个完整的 lamada 表达式:

1
auto f = [](int a) noexcept -> int { return a + 1; };

二、返回类型

很多时候 lamada 表达式的返回值类型是非常明显的,编译器可以根据return语句自动推导出返回类型,这个时候我们可以省略表达式的返回值定义:

1
auto x1 = [](int a) {return a + 1};     // OK: return type is int

但是,初始化列表不能用于返回值的自动推导:

1
auto x2 = [](int a) {return {a+1, a+2}; };   // error: 无法推导出返回值类型

三、捕获列表

lamada 表达式可以通过捕获列表捕获一定范围内的变量:

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

四、异常约束

可以使用noexcept来指定和约束 Lamada 表达内不会抛出异常,如果抛出异常,编译器会产生编译警告。

1
2
3
4
5
6
// throw_lambda_expression.cpp
// compile with: /W4 /EHsc
int main() // 产生编译警告C4297
{
[]() noexcept { throw 5; }();
}

五、mutable

正常情况下,按值捕获的变量,其值在 Lamada 表达式内是不能被修改的(遵循const-by-value),如下面的代码编译会报错:

1
2
3
4
5
6
 int n = 1;
auto fuc = [n]() {
n = 2;
};

// error C3491: “n”: 无法在非可变 lambda 中修改通过复制捕获

可以使用mutable关键字改变这种行为:

1
2
3
4
5
6
int n = 1;
auto fuc = [n]() mutable {
n = 2;
};

printf("%d", n); // 1

虽然在 Lamada 表达式内可以修改n的值,但n仍是按值传递,因此外部n的值没有被改变。

参考:https://msdn.microsoft.com/en-us/library/dd293608.aspx