男女做啊免费视频网站,网站seo优化建议,软件公司都是干什么的,cad室内设计c在c11标准中引入了lambda表达式#xff0c;一般用于定义匿名函数#xff0c;使得代码更加灵活简洁。lambda表达式与普通函数类似#xff0c;也有参数列表、返回值类型和函数体#xff0c;只是它的定义方式更简洁#xff0c;并且可以在函数内部定义。
什么是Lambda表达式…c在c11标准中引入了lambda表达式一般用于定义匿名函数使得代码更加灵活简洁。lambda表达式与普通函数类似也有参数列表、返回值类型和函数体只是它的定义方式更简洁并且可以在函数内部定义。
什么是Lambda表达式
最常见的lambda的表达式写法如下
auto plus [] (int v1, int v2) - int { return v1 v2; }
int sum plus(1, 2);
这里只是计算两个数的和我们一般情况下肯定是不会这么用的更多的时候我们都是和stl的一些算法结合使用例如自定义一个结构体的排序规则和打印。
struct Item
{Item(int aa, int bb) : a(aa), b(bb) {} int a;int b;
};int main()
{std::vectorItem vec;vec.push_back(Item(1, 19));vec.push_back(Item(10, 3));vec.push_back(Item(3, 7));vec.push_back(Item(8, 12));vec.push_back(Item(2, 1));// 根据Item中成员a升序排序std::sort(vec.begin(), vec.end(),[] (const Item v1, const Item v2) { return v1.a v2.a; });// 打印vec中的item成员std::for_each(vec.begin(), vec.end(),[] (const Item item) { std::cout item.a item.b std::endl; });return 0;
}
这样的写法让我们代码更加简洁、清晰可读性更强。
在c的官方文档中给出了lamda表达式的四种写法这里知乎的排版有点难用所以直接在官方文档上截了一个图。 下面介绍一下lambda的四种表达式的含义以及表达式中各个成分的其实说白就是在自己理解的基础上翻译一下官方文档。
四种表达式的含义
1完整的lambda表达式包含了lambda表达式的所有成分。
2常量lambda表达式捕获的变量都是常量不能在lambda表达式的body中进行修改。
3和2基本一致唯一的区别就是lambda表达式的函数返回值可以通过函数体推导出来。一般情况函数返回值类型明确或者没有返回值的情况下可以这样写。
4lambda表达式的函数没有任何参数但是可以添加lambda-specifierslambda-specifiers是什么我们后续再介绍。
lambda表达式各个成员的解释
captures 捕获列表lambda可以把上下文变量以值或引用的方式捕获在body中直接使用。
tparams 模板参数列表(c20引入)让lambda可以像模板函数一样被调用。
params 参数列表有一点需要注意在c14之后允许使用auto左右参数类型。
lambda-specifiers lambda说明符 一些可选的参数这里不多介绍了有兴趣的读者可以去官方文档上看。这里比较常用的参数就是mutable和exception。其中表达式(1)中没有trailing-return-type是因为包含在这一项里面的。
trailing-return-type 返回值类型一般可以省略掉由编译器来推导。
body 函数体函数的具体逻辑。
捕获列表
上面介绍完了lambda表达式的各个成分其实很多部分和正常的函数没什么区别其中最大的一个不同点就是捕获列表。我在刚开始用lambda表达式的时候还一直以为这个没啥用只是用一个 [] 来标志着这是一个lambda表达式。后来了解了才知道原来这个捕获列表如此强大甚至我觉得捕获列表就是lambda表达式的灵魂。下面先介绍几种常用的捕获方式。
[] 什么也不捕获无法lambda函数体使用任何
[] 按值的方式捕获所有变量
[] 按引用的方式捕获所有变量
[, a] 除了变量a之外按值的方式捕获所有局部变量变量a使用引用的方式来捕获。这里可以按引用捕获多个例如 [, a, b,c]。这里注意如果前面加了后面加的具体的参数必须以引用的方式来捕获否则会报错。
[, a] 除了变量a之外按引用的方式捕获所有局部变量变量a使用值的方式来捕获。这里后面的参数也可以多个例如 [, a, b, c]。这里注意如果前面加了后面加的具体的参数必须以值的方式来捕获。
[a, b] 以值的方式捕获a引用的方式捕获b也可以捕获多个。
[this] 在成员函数中也可以直接捕获this指针其实在成员函数中[]和[]也会捕获this指针。 #include iostreamint main()
{int a 3;int b 5;// 按值来捕获auto func1 [a] { std::cout a std::endl; };func1();// 按值来捕获auto func2 [] { std::cout a b std::endl; };func2();// 按引用来捕获auto func3 [a] { std::cout a std::endl; };func3();// 按引用来捕获auto func4 [] { std::cout a b std::endl; };func4();
}
编译器如何看待Lambda表达式
我们把lambda表达式看成一个函数那编译器怎么看待我们协的lambda呢
其实编译器会把我们写的lambda表达式翻译成一个类并重载 operator()来实现。比如我们写一个lambda表达式为
auto plus [] (int a, int b) - int { return a b; }
int c plus(1, 2);
那么编译器会把我们写的表达式翻译为
// 类名是我随便起的
class LambdaClass
{
public:int operator () (int a, int b) const{return a b;}
};LambdaClass plus;
int c plus(1, 2);
调用的时候编译器会生成一个Lambda的对象并调用opeartor ()函数。备注这里的编译的翻译结果并不和真正的结果完全一致只是把最主要的部分体现出来其他的像类到函数指针的转换函数均省略
上面是一种调用方式那么如果我们写一个复杂一点的lambda表达式表达式中的成分会如何与类的成分对应呢我们再看一个 值捕获 例子。
int x 1; int y 2;
auto plus [] (int a, int b) - int { return x y a b; };
int c plus(1, 2);
编译器的翻译结果为
class LambdaClass
{
public:LambdaClass(int xx, int yy): x(xx), y(yy) {}int operator () (int a, int b) const{return x y a b;}private:int x;int y;
}int x 1; int y 2;
LambdaClass plus(x, y);
int c plus(1, 2); 其实这里就可以看出值捕获时编译器会把捕获到的值作为类的成员变量并且变量是以值的方式传递的。需要注意的时如果所有的参数都是值捕获的方式那么生成的operator()函数是const函数的是无法修改捕获的值的哪怕这个修改不会改变lambda表达式外部的变量如果想要在函数内修改捕获的值需要加上关键字 mutable。向下面这样的形式。
int x 1; int y 2;
auto plus [] (int a, int b) mutable - int { x; return x y a b; };
int c plus(1, 2); 我们再来看一个引用捕获的例子。
int x 1; int y 2;
auto plus [] (int a, int b) - int { x; return x y a b;};
int c plus(1, 2);
编译器的翻译结果为
class LambdaClass
{
public:LambdaClass(int xx, int yy): x(xx), y(yy) {}int operator () (int a, int b){x;return x y a b;}private:int x;int y;
};
我们可以看到以引用的方式捕获变量和值捕获的方式有3个不同的地方1. 参数引用的方式进行传递; 2. 引用捕获在函数体修改变量会直接修改lambda表达式外部的变量3. opeartor()函数不是const的。
针对上面的集中情况我们把lambda的各个成分和类的各个成分对应起来就是如下的关系:
捕获列表对应LambdaClass类的private成员。
参数列表对应LambdaClass类的成员函数的operator()的形参列表
mutable对应 LambdaClass类成员函数 operator() 的const属性 但是只有在捕获列表捕获的参数不含有引用捕获的情况下才会生效因为捕获列表只要包含引用捕获那operator()函数就一定是非const函数。
返回类型对应 LambdaClass类成员函数 operator() 的返回类型
函数体对应 LambdaClass类成员函数 operator() 的函数体。
引用捕获和值捕获不同的一点就是对应的成员是否为引用类型。
参考文章:
Lambda expressions
C Lambda 编译器实现原理