定义

定义一个 lambda 匿名函数很简单,可以套用如下的语法格式:

1
2
3
4
[外部变量访问方式说明符] (参数) mutable noexcept/throw() -> 返回值类型
{
函数体;
};

mutable

此关键字可以省略,如果使用则之前的 () 小括号将不能省略(参数个数可以为 0)。默认情况下,对于以值传递方式引入的外部变量,不允许在 lambda 表达式内部修改它们的值(可以理解为这部分变量都是 const 常量)。而如果想修改它们,就必须使用 mutable 关键字。

注意,对于以值传递方式引入的外部变量,lambda 表达式修改的是拷贝的那一份,并不会修改真正的外部变量;

noexcept/throw()

可以省略,如果使用,在之前的 () 小括号将不能省略(参数个数可以为 0)。默认情况下,lambda 函数的函数体中可以抛出任何类型的异常。而标注 noexcept 关键字,则表示函数体内不会抛出任何异常(若有异常会导致崩溃);使用 throw() 可以指定 lambda 函数内部可以抛出的异常类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;
int main()
{
auto except = []()throw(int) {
throw 10;
};
try {
except();
}
catch (int) {
cout << "捕获到了整形异常";
}
return 0;
}

捕获

Lambda 可在其主体中引入新的变量(用 C++14),它还可以访问(或“捕获”)周边范围内的变量。

1
2
3
4
5
6
7
8
9
10
#include <algorithm>
#include <cmath>

void abssort(float* x, unsigned n) {
std::sort(x, x + n,
[](float a, float b) {
return (std::abs(a) < std::abs(b));
}
);
}

指定要捕获的变量以及是通过值还是引用进行捕获。 有与号 (&) 前缀的变量通过引用访问,没有该前缀的变量通过访问。

以使用默认捕获模式(标准语法中的 capture-default)来指示如何捕获 lambda 中引用的任何外部变量:[&] 表示通过引用捕获引用的所有变量,而 [=] 表示通过值捕获它们。 可以使用默认捕获模式,然后为特定变量显式指定相反的模式。 (引用和值,效果同函数形参)

例如,如果 lambda 体通过引用访问外部变量 total 并通过值访问外部变量 factor,则以下 capture 子句等效:

1
2
3
4
5
6
[&total, factor]
[factor, &total]
[&, factor]
[factor, &]
[=, &total]
[&total, =]

identifier(&/=)this 在 capture 子句中出现的次数不能超过一次。示例:

1
2
3
4
5
6
7
8
struct S { void f(int i); };

void S::f(int i) {
[&, i]{}; // OK
[&, &i]{}; // ERROR: i preceded by & when & is the default
[=, this]{}; // ERROR: this when = is the default
[i, i]{}; // ERROR: i repeated
}

Lambda可变参数模板

1
2
3
4
5
template<class... Args>
void f(Args... args) {
auto x = [args...] { return g(args...); };
x();
}

参数列表

1
2
3
4
int y = [] (int first, int second)
{
return first + second;
};
1
2
3
4
5
// C++14,创建模板
auto y = [] (auto first, auto second)
{
return first + second;
};

在 C++14 中,如果参数类型是泛型,则可以使用 auto 关键字作为类型说明符。 这将告知编译器将函数调用运算符创建为模板。 参数列表中的每个 auto 实例等效于一个不同的类型参数。

异常规范

1
2
3
4
int main() // C4297 expected
{
[]() throw() { throw 5; }();
}

返回类型

1
2
3
4
auto x1 = [](int i){ return i; }; // OK: return type is int

auto x2 = []{ return{ 1, 2 }; }; // ERROR: return type is void, deducing
// return type from braced-init-list is not valid

lambda 表达式可以生成另一个 lambda 表达式作为其返回值。

通过值显式捕获变量 n 并通过引用隐式捕获变量 m 的 lambda 表达式:

1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;

int main()
{
int m = 0;
int n = 0;
[&, n] (int a) mutable { m = ++n + a; }(4);
cout << m << " " << n << endl; // 输出:5 0
}

mutable 规范允许在 lambda 中修改 n。

示例:使用 generate 函数和 lambda 表达式为 vector 对象中的每个元素赋值。 lambda 表达式将修改静态变量以生成下一个元素的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>
using namespace std;

void fillVector(vector<int>& v)
{
static int nextValue = 1;

generate(v.begin(), v.end(), [] { return nextValue++; }); // generate 函数见:生成
//WARNING: not thread-safe and …
}
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
template <typename C>
void print(const string& s, const C& c) {
cout << s;
for (const auto& e : c) {
cout << e << " ";
}
cout << endl;
}

int main()
{
const int elementCount = 9;
vector<int> v(elementCount, 1);
int x = 1, y = 1;

//斐波那契:每个等于前两个的和
generate_n(v.begin() + 2, elementCount - 2,
[=]() mutable throw() -> int {
int n = x + y;
x = y; y = n;
return n;
});
print("vector v after call to generate_n() with lambda: ", v);

cout << "x: " << x << " y: " << y << endl; // x y 仍为1

fillVector(v);
print("vector v after 1st call to fillVector(): ", v);

fillVector(v);
print("vector v after 2nd call to fillVector(): ", v);
}

输出:

1
2
3
4
vector v after call to generate_n() with lambda: 1 1 2 3 5 8 13 21 34
x: 1 y: 1
vector v after 1st call to fillVector(): 1 2 3 4 5 6 7 8 9
vector v after 2nd call to fillVector(): 10 11 12 13 14 15 16 17 18

参考:https://msdn.microsoft.com/zh-cn/library/dd293608.aspx