-
-
-
- Lambda 表达式探究
- 基本的语法格式
- 异同
- 向lambda 表达式传递参数
- 使用捕获列表
- 引用捕获
- Lambda 表达式探究
-
-
Lambda 表达式探究
第一次遇到lambda表达式是在学习
C++ Primer
时,当时是在STL算法
那个章节,感兴趣的可以自行查阅。今天在学习Cocos2D-X
时再次遇到这个东西。我第一遍接触这个表达式其实很震惊的,后来几经查阅资料,稍微有点明白,但是长时间不用变又忘记了,所以当我再次遇到的时候我决定把它总结一下,加深记忆。
基本的语法格式:
[]()->return_type{};
这个就是lambda
表达式的基本语法结构。看起来很复杂,但是一旦了解了之后就会明白真正复杂的东西只要我们不是自找麻烦,那么用起来还是相对比较简单明了的。
在C++Primer
中,我们把这个表达式当作可调用对象。在未接触此表达式之前,我们能接触的可调用表达式一般有三个或两个。常见的是funtion ,Pointer to function
如果你不熟悉类的话你可能不知道class
也是一个可调用对象,只是前提你要重载可调用运算符。今天我们接触的这个也是可以调用对象,而且它们和类的关系是比较密切的,在C++ Primer
的某章节中曾经详细介绍过他们之间的关系。这个地方就不搬运了。
上文我们已经提到过了,此表达式同属于可调用对象,那么我们可以类别比对函数的理解来理解lambda
表达式。我曾经看到过如下观点直接把此表达式解释为匿名函数,据说java
中也有类似的存在。一旦我们能把握它的函数性质,理解起来就很容易了。
在来看一下最基本的语法结构。
[capture_lists](parameters)
->return_type{function_body};
从[]
开始解释。这个方括号里面存在捕获列表。
接下来是()
接下来的和传统函数一直,括号里面可以存放参数列表。
后面是->return_type
尾置返回类型。不详细讲,不了解的可以查资料。
最后面的就更简单的了。是存放我们的函数体。
如果砍去捕获列表的话,同普通函数区别不是很大。只不过是用了尾置返回类型而已,但是这种尾置返回也不算是很新奇的东西。
异同:
1.我们可以在函数体内定义一个lambda
表达式,但是函数是万万不能的,嵌套定义可以大忌。
2.必须使用尾置返回类型,但是普通函数却不是必须的。
示例:
#include<iostream>
int main(){
auto func=[](){std::cout<<88<<std::endl;};
//Call func ;
func();
return 0;
}
我们在main函数内部定义了一个lambda
表达式,并且用它初始化了一个func
.我们使用了auto
进行自动推断。上面我省略了尾置返回类型。
记住一点,我们可以省略参数列表和尾置返回类型,但是余下的不可省略。
向lambda
表达式传递参数:
类似于普通函数,满足实参初始化形参的规则。但是有一点异同,此表达式的形参不能有默认值。
示例:
#include <iostream>
int main(){
auto func = [](const int& arg){std::cout <<arg<< std::endl; };
func(13);
return 0;
}
例子很简单,我们可以看书参数列表中有了参数,同普通函数很相似。
当我们忽略返回类型的时候,如果lambda
函数体包含了任何单一return
语句之外的内容而且未指定返回类型时,会自动推断为void
类型。意思就是说如果只有单一的return
语句,那么会根据返回的值进行自动推断。
使用捕获列表:
首先确立一个观点,捕获只能捕获其所在函数体内中的局部变量,并且是不包括static local variable
(静态局部比变量)。捕获列表以[]
开始,其中可以用逗号分隔开捕获的参数。我们可以省略参数名称,用简单的符号进行替代。
值捕获:
当我们直接把要捕获的参数写到[]
中,那么我使用的就是值捕获。
示例:
#include <iostream>
int main(){
int number = 10;
auto func = [number](const int arg){std::cout <<arg+number<< std::endl; };
func(13);
return 0;
}
引用捕获:
类似我们在函数同学习的形参。所以直接举个例子:
#include <iostream>
int main(){
int number = 10;
auto func = [&number](const int arg){std::cout <<arg+number<< std::endl; };
func(13);
return 0;
}
对比上面可以发现只是简单的加上了&
符号而已。
我们要记住一点。值捕获中被捕获的变量的值是在lambda
表达式被创建的时候拷贝,所以后续的修改对其无影响。但是引用使用的一份示例,所以修改会带到此表达式之外。
我们可以返回函数指针类型,那么我也可以返回一个lambda
表达式,只是我未曾看过,也未曾写过类似的函数。如果函数返回一个lambda
,那么与函数不能返回局部变量的引用类似,此表达式中不能包含引用捕获。
C++ Primer
中有如下建议,保持捕获的简单化,避免捕获指针或者引用。然而今天看的Cocos2D-X
代码却是违反了这个建议。
最后介绍下隐式捕获,同上面介绍的捕获是一个意思,知识稍微腼腆一点而已。我们不在写上参数名,饿时直接用符号代替
示例:
auto func = [&](const int arg){std::cout <<arg+number<< std::endl; };
auto func = [=](const int arg){std::cout << arg + number << std::endl; };
我们使用了&,=
符号代替值捕获和引用捕获。需要注意的是混合使用隐式捕获和显式捕获。当我们遇到[&,identifier_list]
时。后面的identifier_list
不可以使用引用捕获,只能使用值捕获。同样的[=,identifier_list]
不可以使用值捕获,只能使用引用捕获。只是一些规则而已。
可变lambda
:
当我们使用值捕获的时候,通常是不需要修改捕获的参数,但是想修改的时候记得加上mutable
关键字。
示例:
auto func = [=](const int& arg) mutable {std::cout << arg + number << std::endl; };
May 4, 2015 6:18 PM By Max
我发现CSDN的MarkDown不是很好用,所以我把源文件分享下,可以下载下来看,我使用的是Haroopad,挺好用的编辑器。
源文件下载地址:戳我下载源文件