Lambda
C++11 中 lambda 是一个匿名函数对象
最简形式
[]{ cout << "lambda" << endl; }(); // print "lambda" auto l = []{ cout << "lambda" << endl; }; ... l(); // print "lambda"
完整形式
[...](...) mutable throwSpec ->retType {...}
[...]:lambda 引入符,捕获 non-static 外部变量
[] 不捕获外部变量
[=] 传值
[&] 传引用
[x, &y] x 传值,y 传引用
[=, &x] x 传引用,其余变量传值(尽量不这样写默认情况,而是全部列出,增加代码可读性)
(...):形参列表,若含以下可选参数之一,即使无形参,也必须加上 ()
可选参数
mutable:关键字,表明传入的外部变量在函数内可改动。若传值捕获外部变量,再加上 mutable 关键字,表明函数内变量可改动,但不会影响函数外该变量的值。
throwSpec:异常处理
->retType:lambda 的返回类型
{...}:函数体,函数体内可声明 static / non-static 变量,可返回数值
例子
1 int main() { 2 int id = 0; 3 auto f = [id]() mutable { 4 cout << "f() id: " << id << endl; 5 ++id; 6 }; 7 id = 99; 8 f(); // print "f() id: 0" 9 f(); // print "f() id: 1" 10 f(); // print "f() id: 2" 11 cout << "id: " << id << endl; // print "id: 99" 12 return 0; 13 }
注意:
1、编译器看到第 3 行的时候,id = 0,由于传值,所以函数内 id = 0
2、若不写 mutable,则 ++id 编译报错 “cannot assign a variable captured by copy in a non-mutable lambda”
3、若 id 定义为 static,则 static int id = 0 编译报错 “id cannot be captured because it does not have automatic storage duration(静态存储周期)”
比较(传引用)
1 int main() { 2 int id = 0; 3 auto f = [&id]() mutable { 4 cout << "f() id: " << id << endl; 5 ++id; 6 }; 7 id = 99; 8 f(); // print "f() id: 99" 9 f(); // print "f() id: 100" 10 f(); // print "f() id: 101" 11 cout << "id: " << id << endl; // print "id: 102" 12 return 0; 13 }
比较(有形参)
1 int main() { 2 int id = 0; 3 auto f = [id](int p) { 4 cout << "f() id: " << id << " p: " << p << endl; 5 ++p; 6 }; 7 id = 99; 8 f(id); // print "f() id: 0 p: 99" 9 f(7); // print "f() id: 0 p: 7" 10 cout << "id: " << id << endl; // print "id: 99" 11 return 0; 12 }
声明一个 lambda 对象,需要使用 template 或 auto。
如果需要 lambda 对象的类型,需要使用 decltype(),例如,将 lambda 作为哈希函数或排序准则,传给一个关联容器或无序容器时。
auto cmp = [](const Person& p1, const Person& p2) { return p1.lastname < p2.lastname || (p1.lastname == p2.lastname && p1.lastname < p2.lastname); } ... std::set<Person, decltype(cmp)> coll(cmp); // set的声明需要cmp的类型
这里必须使用 cmp 作为 set 构造函数的参数,否则编译器会尝试调用 cmp 对象的默认构造函数并报错。
注意,虽然 lambda 是一个函数对象,但无法为其设计默认构造函数和赋值操作。