1. lambda 表达式的语法
[捕捉列表] (参数列表) mutable ->return_type {函数体}
(参数列表)
:与函数传参一致。不需要传递参数时,可以连同()
省略。mutable
:传值捕捉时,mutable 可以取消参数的常性,使其在函数体内能被修改。不使用时,可以省略;使用该修饰符时,(参数列表)
不可省略(即使参数列表为空)。->return_type
:函数体的返回值类型,通常可以省略,由编译器自行推导。
C++11 中,最简单的 lambda 表达式为
[]{}
,但它没有任何作用。
1.2 lambda 用于sort的一个场景
struct Goods { string _name;// 名称 double _price;// 价格 double _evaluations;// 评价 Goods(const string& name, double price, double evaluations) :_evaluations(evaluations) , _price(price) , _name(name) {} };
int main() { vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } }; auto price = [](const Goods& g1, const Goods& g2) { return g1._price > g2._price; }; auto evaluation = [](const Goods& g1, const Goods& g2) { return g1._evaluations > g2._evaluations; }; // 按价格排降序 sort(v.begin(), v.end(), price); // 按评价排降序 sort(v.begin(), v.end(), evaluation); return 0; }
通过以上场景,可以看出 lambda表达式 实际上是匿名仿函数。
2. 值传递捕捉/引用传递捕捉与 mutable
2.1 值传递捕捉 和 mutable
int main() { int x = 10, y = 20; // 值传递捕捉 x auto fun1 = [x] { // ++x; // “x”: 无法在非可变 lambda 中修改通过复制捕获 }; // 使用 mutable 时,(参数列表) 不可省略 auto fun2 = [x]()mutable { // x += (y + 2);// 无法隐式捕获“y”,因为尚未指定默认捕获模式 x += 2; cout << x << endl; }; fun2();// 函数体内的 x 是一个临时变量, cout << x << endl;// 修改函数体内的 x 不会改变 被捕捉的/函数体外的 x // 捕捉列表为 = 时,可以以值传递捕捉的方式,隐式捕捉当前作用域所有可访问的对象 auto fun3 = [=] { return x*2 + y; };// 省略 ->return_type 时,编译器会自动推导 int tmp = fun3(); cout << tmp << endl; return 0; }
2.2 引用传递捕捉
int main() { int x = 10, int y = 20; // 引用传递捕捉 x auto fun1 = [&x] { ++x; cout << x << endl; }; fun1(); cout << x << endl; // 捕捉列表为 & 时,可以以引用传递捕捉的方式,隐式捕捉当前作用域所有可访问的对象 auto fun2 = [&] { y++; x += y; }; fun2(); cout << x << endl; cout << y << endl; return 0; }
PS:
- “当前作用域”:指包含该 lambda表达式的作用域。
- 语法上,捕捉列表可由多个捕捉对象组成,以逗号分割。
int main() { int x, y; // 引用传递捕捉 x,值传递捕捉 y auto add_x = [&x, y](int a) mutable { cin >> y; x = y*2 + a; }; add_x(10); return 0; }
- 捕捉列表不允许变量重复传递,否则会导致编译出错。
int main() { int x = 1, y = 2; // [=, x] {};// 重复捕捉 x // error [=, &x] {}; // 引用传递捕捉 x,值传递捕捉当前作用域的其他可访问对象 // [&, &x] {}; // error [&, x] {};// 值传递捕捉 x,引用传递捕捉当前作用域的其他可访问对象 return 0; }
- 块作用域外的 lambda表达式 捕捉列表必须为空。
auto func = [] { cout << "Hello World!" << endl; }; int main() { func(); return 0; }
- lambda表达式 之间不能相互赋值。
int main() { auto f1 = [] { cout << "hello world!" << endl; }; auto f2 = [] { cout << "hello world!" << endl; }; // f2 = f1;// error auto f3(f1); return 0; }
3. 函数对象 与 lambda表达式
调用 f1()
f2()
的反汇编:
注意看:<lambda_xxxx>::operator()
,operator()
的方式不是类重载“函数调用运算符”吗?
也就意味着,如果定义了一个 lambda表达式,编译器会自动生成一个类,重载 operator()
,且 lambda表达式 的名称是编译器根据特定算法实时生成的、保证不重复。
此处解释了,“为什么 lambda表达式 之间不能相互赋值”。