前两篇在这里:
C++11新特性之新类型与初始化: http://blog.guoyb.com/2016/06/18/cpp11-1/
C++11新特性之类型推断与类型获取: http://blog.guoyb.com/2016/06/25/cpp11-2/
这是C++11新特性介绍的第三部分,涉及到C++11这次更新中较为重要的lambda表达式。不想看toy code的读者可以直接拉到文章最后看这部分的总结。
熟悉Python的程序员应该对lambda不陌生。简单来说,lambda就是一个匿名的可调用代码块。在C++11新标准中,lambda具有如下格式:
[capture list] (parameter list) -> return type { function body }
可以看到,他有四个组成部分:
其中,参数列表和返回类型可以忽略。
下面,具体看几个简单的例子:
auto f1 = [] { return 1; };
auto f2 = [] () { return 2; };
cout<<f1()<<'/t'<<f2()<<endl;
lambda中的捕获列表既可以捕获值,也可以捕获引用。
捕获值:
int test_data[] = {1, 5, 9, 7, 3, 19, 13, 17};
int border = 8;
auto f3 = [border](const int &i){ if(i > border) cout<<i<<'/t'; };
for_each(begin(test_data), end(test_data), f3);
cout<<endl;
捕获引用:
auto f4 = [&border](const int &i){ if(i > border) cout<<i<<'/t'; };
border = 6;
for_each(begin(test_data), end(test_data), f4);
cout<<endl;
通过输出可以看出,lambda中起作用的border是修改后的6,证实了捕获的确是是引用。需要注意的是,在捕获引用时,需要保证当lambda被调用时,此引用仍然有效。
捕获列表还可以采用 隐式捕获 的方式,即让编译器通过lambda的执行代码来判断需要捕获哪些局部变量。
隐式捕获可以捕获值、引用或者两者混合:
char space = ' ';
auto f5 = [=](const int &i){ if(i > border) cout<<i<<'/t'; };
auto f6 = [&](const int &i){ if(i > border) cout<<i<<'/t'; };
auto f7 = [&, space](const int &i){ if(i > border) cout<<i<<space; };
border = 0;
for_each(begin(test_data), end(test_data), f5);
cout<<endl;
for_each(begin(test_data), end(test_data), f6);
cout<<endl;
for_each(begin(test_data), end(test_data), f7);
cout<<endl;
这里的f7使用的混合形式,可以读作“除了space捕获值之外,其他变量均捕获引用”。
当lambda需要在其中修改被值捕获的变量的值时,需要给lambda加上mutable关键字。否则会有编译错误。
auto f8 = [&, space](const int &i) mutable { if(i > border) {cout<<i<<space; space='/t';} };
for_each(begin(test_data), end(test_data), f8);
cout<<endl;
cout<<1<<space<<2<<endl;
从输出中可以看出,space在lambda f8中的值,在第一次调用之后,就被变成了制表符Tab;但是在lambda之外,space仍然是空格。
lambda的返回类型采用尾置返回类型的方式。一般的:
但是,经过实际测试,目前的g++编译器更聪明了:对于第2点,目前只要编译器可以从lambda函数体中推断出函数的返回类型,就不需要显式指定返回类型,例如:
auto f9 = [](const int i){if(i % 3) return i * 3; else return i;};
transform(begin(test_data), end(test_data), begin(test_data), f9);
border = 0;
for_each(begin(test_data), end(test_data), f6);
cout<<endl;
lambda代码块中有多个return语句,并且还有if/else语句,但是编译器可以根据return语句推断出,其返回值应该是一个int类型,所以可以省略尾置返回类型。
但是,像下面这种形式,由于编译器在推断返回类型时发现了不一致,所以必须显式的指定返回类型:
auto f10 = [](const int i) -> double
{if(i % 5) return i * 5.0; else return i;};
transform(begin(test_data), end(test_data), begin(test_data), f10);
for_each(begin(test_data), end(test_data), f6);
cout<<endl;
完整代码详见 lambda_expr.cpp
转载请注明出处: http://blog.guoyb.com/2016/06/30/cpp11-3/
欢迎使用微信扫描下方二维码,关注我的微信公众号TechTalking,技术·生活·思考: