lambda
的语法比较简单,下面这个 lambda
返回一个字符串。
auto lambda = []{ return "hello world"; };
lambda(); //将返回 hello world 字符串
lambda
还可以传参数。
auto lambda = [](std::string str){ return str; };
lambda("hello world"); //将返回 hello world 字符串
可以看到 lambda
就像一个函数一样,可以接受参数和返回值,因为 lambda
就是一个匿名函数。它可以方便我们写出函数式的代码。
lambda
的语法一共有如下三种。
[ captures ] ( parameters ) -> return_type { body }
[ captures ] ( parameters ) { body }
[ captures ] { body }
语法中的关键词意义如下。
- parameters:
lambda
表达式接受的参数,就像函数参数一样 - captures: 捕获变量,捕获变量的方式有两种:按引用捕获和按复制捕获
- body: 语句块
常见的 lambda
表达式的写法如下。一般情况是不需要显式写出返回类型的,代码也会更简洁。如果 lambda
表达式没有参数的时候括号也是可以省略的。
[](int i){ return i; }
[](int i) ->int { return i; }
[](){ return 0; }
[]{ return 0; }
按引用捕获需要在 [ ]
内写 &
,按复制捕获为 =
。若什么都不写则不捕获外部变量。
int a = 1;
int b = 2;
[&a](int i){ return i; } //按引用捕获变量 a
[a](int i){ return i; } //按复制捕获变量 a
[=](int i){ return i; } //按复制捕获变量 a,b
[&, b](int i){ return i; } //按引用捕获变量,除了 b(b 按复制捕获)。
接下来在 code1 目录下新建一个 code0.cpp 文件。:
#include <iostream>
int main() {
auto lambda = [] {
std::cout << "hello lambda!" << std::endl;
};
lambda();
}
编译和运行代码:在 「build」 目录下执行
g++ ../code0.cpp -o code0 -std=c++11 && ./code0
输出结果:
hello lambda!
这是最简单的一个 lambda
,接下来看其它几种形式的 lambda
,打开并编辑 code1.cpp 文件:
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
auto lambda = []{ std::cout<<"hello lambda!"<<std::endl; };
lambda();
auto lambda1 = [](int i)->int { return i; };
auto lambda2 = [](int i) { return i; };
std::cout<<lambda1(1)<<std::endl;
std::cout<<lambda2(2)<<std::endl;
std::vector<int> v{1,2,3,4,5,6,7};
std::for_each(v.begin(), v.end(), [](int i){
std::cout<<i<<" ";
});
std::cout<<std::endl;
return 0;
}
编译和运行代码:在 build 目录下执行
g++ ../code1.cpp -o code1 -std=c++11 && ./code1
输出结果:
hello lambda!
1
2
1 2 3 4 5 6 7
上面是 lambda
用于 stl 算法的经典例子。std::for_each
的时候,传入一个 lambda
,用它把 vector
容器中的每个整数打印出来,这是 lambda
的一个典型应用场景,使用 lambda
让我们的代码变得简单清晰。
接下来我们实验一下 lambda
捕获变量的情况,在 code1 目录下新建 code2_1.cpp 文件:
#include <iostream>
int main() {
int a = 1;
int b = 2;
int c = 3;
auto lambda1 = [&]{
a = 4;
b = 5;
c = 6;
};
lambda1();
std::cout<<a<<" "<<b<<" "<<c<<std::endl;
}
编译和运行代码:在 build 目录下执行
g++ ../code2_1.cpp -o code2_1 -std=c++11 && ./code2_1
输出结果:
4 5 6
可以看到我们通过引用方式捕获变量后,就可以对变量进行修改了。接下来看按拷贝方式捕获的情况,在 code1 目录下新建 code2_2.cpp 文件:
#include <iostream>
int main() {
int a = 1;
int b = 2;
int c = 3;
auto lambda1 = [&]{
a = 4;
b = 5;
c = 6;
};
lambda1();
std::cout<<a<<" "<<b<<" "<<c<<std::endl;
auto lambda2 = [a,b,c]() mutable{
a = 1;
b = 2;
c = 3;
std::cout<<"in lambda2 :"<<a<<" "<<b<<" "<<c<<std::endl;
};
lambda2();
std::cout<<a<<" "<<b<<" "<<c<<std::endl;
auto lambda3 = [=]() mutable{
a = 10;
b = 20;
c = 30;
std::cout<<"in lambda3 :"<<a<<" "<<b<<" "<<c<<std::endl;
};
lambda3();
std::cout<<a<<" "<<b<<" "<<c<<std::endl;
}
编译和运行代码:在 build 目录下执行
g++ ../code2_2.cpp -o code2_2 -std=c++11 && ./code2_2
输出结果:
4 5 6
in lambda2 :1 2 3
4 5 6
in lambda3 :10 20 30
4 5 6
可以看到 lambda2
中 a
,b
,c
按复制捕获,在 lambda
中修改 a
,b
,c
是不会修改外面的 a
,b
,c
的值。
lambda
在类中使用的时候需要捕获 this
指针才能访问类成员。
ambda
按引用捕获变量时要注意临时变量生命周期的问题,我们通过一个实验来看这个问题,在 code1 目录下新建 code3.cpp 文件,编写下面这些代码:
#include <iostream>
#include <string>
#include <functional>
std::function<void()> test(){
std::string str = "hello";
auto lambda = [&str]{
std::cout<<str<<" "<<"lambda";
};
return lambda;
}
int main() {
auto lambda1 = test();
lambda1();
}
编译和运行代码:在 build 目录下执行
g++ ../code3.cpp -o code3 -std=c++11 && ./code3
输出结果:
将不会输出字符串,因为 test
函数中的 lambda
捕获的是一个临时变量 str
的引用,当 test
函数结束时 str
被析构,这时 lambda
捕获的 str
就变成一个无效的字符串,因此在 main
中再调用 lambda1
时是不会打印出字符串的。
摘自:实验课网站《C++ 实现 RPC 网络通讯库》