26. lambda 表达式

根据算法接受一元谓词(unary predicate)还是二元谓词(binary predicate),我们传递给算法的谓词必须严格接受一个或两个参数。 例如,sort 接受二元谓词。

lambda 表达式可以在一定程度上解决这个问题。lambda 表达式表示一个可调用的代码单元。可以将其理解为一个未命名的内联函数。 lambda 表达式具有返回类型、参数列表、函数体。与普通函数不同的是,其参数列表不能有默认参数,且使用尾置返回 -> 来指定返回类型。 lambda 可能定义在函数内部。

[capture list] (parameter list) -> return type {function body}

可以忽略参数列表和返回类型,但是必须包括捕获列表和函数体。

  • 捕获列表是 lambda 所在的函数内部定义的局部变量,即 lambda 表达式需要使用的变量,通常为空。

  • 如果忽略返回类型,lambda 根据函数体中的代码推断出返回类型:

    • 如果函数体只有一个 return 语句,则返回类型从 return 表达式的类型推断。

    • 否则,返回 void。

auto f = [] {return 42;}; // 返回 int
cout << f() << endl; // 打印 42

Note

可调用对象

对于一个对象或表达式,如果可以对其使用调用运算符 () ,则称其为可调用对象。

可调用对象包括:函数、函数指针、重载了运算符 () 的类、lambda 表达式。

26.1. 捕获列表

类似于参数传递,变量的捕获方式可以是值捕获或引用捕获。

捕获列表:

  • [] :空捕获列表,lambda 不能使用所在函数中的变量。

  • [names] :使用逗号分隔的名字列表,默认为值捕获(拷贝),名字前加 & 表示引用捕获。

  • [=] :隐式值捕获,将拷贝 lambda 表达式所使用的来自所在函数中的变量。

  • [&] :隐式引用捕获,将引用 lambda 表达式所使用的来自所在函数中的变量。

  • [=, identifier_list] :identifier_list 声明的变量采用引用捕获,其他使用的变量采用值捕获。

  • [&, identifier_list] :identifier_list 声明的变量采用值捕获,其他使用的变量采用引用捕获。

值捕获

捕获变量的值是在 lambda 创建时 拷贝,而不是调用时拷贝。

1void fcn1()
2{
3    size_t v1 = 42;
4    auto f = [v1] {return v1;}; // 拷贝 v1
5    v1 = 0;
6    auto j = f(); // j = 42
7}

引用捕获

1void fcn2()
2{
3    size_t v1 = 42;
4    auto f = [&v1] {return v1;}; // 引用 v1
5    v1 = 0;
6    auto j = f(); // j = 0
7}

26.2. 例子

给定一个值 ref,将数组元素按照与 ref 的差值从小到大排序。

1void Sort_(vector<int>& vec, int ref)
2{
3    sort(vec.begin(), vec.end(), [ref](int a, int b){return abs(a - ref) < abs(b - ref);});
4}

26.3. 参考资料

1.《C++ Primer 第5版 中文版》 Page 344 – 353。