C++ Lambda函数

Lambda的概念起源于数学中的Lambda演算(也叫λ 演算),当然,我们今天要讨论的不是数学(我也不懂J),而是编程。Lambada在程序设计中指的是‘匿名函数’。 简单来说,编程中提到的 lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。 我们今天就来看看C++ 11中引入的Lambda 表达式。

引子

为了直观,我们先看一个例子:C++的标准库中有一个常用算法的库,其中提供了很多算法函数,比如 sort()。我们先来看看sort的一般使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// sort algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::sort
#include <vector> // std::vector

bool myfunction (int i,int j) { return (i<j); }

struct myclass {
bool operator() (int i,int j) { return (i<j);}
} myobject;

int main () {
int myints[] = {32,71,12,45,26,80,53,33};
std::vector<int> myvector (myints, myints+8); // 32 71 12 45 26 80 53 33

// using default comparison (operator <):
std::sort (myvector.begin(), myvector.begin()+4); //(12 32 45 71)26 80 53 33

// using function as comp
std::sort (myvector.begin()+4, myvector.end(), myfunction); // 12 32 45 71(26 33 53 80)

// using object as comp
std::sort (myvector.begin(), myvector.end(), myobject); //(12 26 32 33 45 53 71 80)

// print out content:
std::cout << "myvector contains:";
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';

return 0;
}

可以看到在使用function comparison或者object comparison的时候,我们都需要提前先定义好comparison函数。 这其实是一个(污染环境的)函数,尤其如果这个函数只会使用一次的话。

而如果使用Lambda函数会怎么样呢?

1
2
// using Lambda函数
std::sort (myvector.begin(), myvector.end(), [](int i,int j) { return (i<j); }); //(12 26 32 33 45 53 71 80)

可以看到,这种写法实际上更易读,因为这个comparison函数具体是要做什么,非常一目了然。如果你仔细观察自己的代码,会发现这种场景其实很常见:你在某处就真的只需要一个能做一件事情的函数而已,连它叫什么名字都无关紧要。Lambda 表达式就可以用来做这件事。

利用 lambda 表达式创建那些“只用一次”或者比较短小的函数非常方便和高效。

语法

下面来看一下Lambda函数如何使用:

1
2
3
4
[ capture ] ( params ) mutable exception attribute -> ret { body }	(1)	
[ capture ] ( params ) -> ret { body } (2)
[ capture ] ( params ) { body } (3)
[ capture ] { body } (4)

1) 完整的声明

2) 一个常lambda的声明:按副本捕获的对象不能被修改。

3) 省略后缀返回值类型:闭包的operator()的返回值类型是根据以下规则推导出的:

如果body仅包含单一的return语句,那么返回值类型是返回表达式的类型(在此隐式转换之后的类型:右值到左值、数组与指针、函数到指针)
否则,返回类型是void
4) 省略参数列表:函数没有参数,即参数列表是()

解释:

  • mutable - 允许body修改传值进来的形参,以及调用它们的非常成员函数。
  • exception - 提供闭包类型的operator()成员函数的异常说明或noexcept语句
  • attribute - 提供闭包类型的operator()成员函数的属性说明
  • capture - 指定哪些在函数声明处的作用域中可见的符号将在函数体内可见。

    符号表可按如下规则传入:

    • [] : 不捕获任何外部变量
    • [&] : 通过引用方式捕获所有外部变量
    • [=] : 通过拷贝方式捕获所有外部变量
    • [=, &foo] : 通过引用方式捕获 foo 变量,其他外部变量通过拷贝方式捕获
    • [bar] : 通过拷贝方式捕获 bar 变量
    • [this] : 捕获当前类的 this 指针
  • params - 参数列表,与命名函数一样
  • ret - 返回值类型。如果不存在,它由该函数的return语句来隐式决定(或者是void,例如当它不返回任何值的时候)
  • body - 函数体

Reference