当前位置: 首页 > 科技观察

编译器如何实现lambda表达式?

时间:2023-03-19 14:07:40 科技观察

本文转载自微信公众号《程序喵大师》,作者程序喵大师。转载本文请联系程序大师喵公众号。Lambda表达式是在C++11中引入的。用lambda表达式表达匿名函数非常方便。语法非常简单,可以使代码更紧凑,更易读。Lambda表达式更适合定义小的内联回调并将它们传递给其他函数,而不是在别处定义一个完整的函数对象,并在其重载的函数调用运算符中实现回调逻辑。所有的逻辑都集中在一个地方,易于理解和维护。Lambda表达式可以接收参数、返回值,并且可以模板化。外部变量可以通过值或引用来访问,非常灵活。关于lambda表达式的使用,之前有介绍过。您可以阅读本文以了解c++11std::function和lambda表达式的新特性。这里简单介绍一下:autolambda{[]{cout<<"Hello\n";}};拉姆达();这个lambda表达式是如何实现的?编译器会自动将lambda表达式转换为函数对象,编译器会为其生成一个唯一的名称。上面的例子会自动转换成下面的函数对象。注意函数调用操作符是一个const方法,返回类型是auto,方便编译器自动从方法体中推导出返回类型。classCompilerGeneratedName{public:autooperator()()const{cout<<"Hello\n";}};编译器生成的lambda闭包的名字会是一些奇怪的名字,比如__Lambda_21Za等,这个名字我们无从得知,我们也不需要知道这个名字。lambda表达式可以接收参数,参数在括号中指定,就像普通函数一样。下面是一个例子:autolambda{[](intvalue){cout<<"Thevalueis"<{returna+b;}};intsum=lambda(11,22);编译器把它变成这样:classCompilerGeneratedName{public:autooperator()(inta,intb)const{returna+b;}};能捕获变量的lambda表达式是怎么实现的?例如,下面的lambda表达式:doubledata{1.234};autolambda{[data]{cout<<"Data="<autooperator()(constT1&value1,constT2&value2)const{returnvalue1==value2;}};如果findMatches()函数中的参数是其他类型,则areEqual泛型表达式可以不做任何改动直接继续说完编译器是如何实现lambda表达式的,下面介绍lambda表达式的捕获方法。从闭包作用域捕获所有变量有两种方式,称为默认捕获:[=]值捕获所有变量[&]引用捕获所有变量注意:使用引用方式捕获变量时,必须保证引用在lambda表达式执行过程中是合法的。使用默认捕获时,按值(=)或引用(&),只会捕获那些在lambda表达式中实际使用的变量,不会捕获未使用的变量。不建议使用默认捕获,即使默认捕获只捕获那些在lambda表达式主体中实际使用的变量,使用=默认捕获,你可能会不小心造成昂贵的副本,使用&默认捕获,你可能不小心在闭包范围内修改变量,建议明确指定要捕获哪些变量以及如何捕获。再次注意:全局变量总是通过引用捕获。例如,在下面的代码中,默认捕获用于按值捕获所有内容。但是,全局变量global实际上是通过引用捕获的,它的值是在lambda执行后改变的。intglobal{42};intmain(){autolambda{[=]{global=2;}};lambda();//这里global是2!}不允许像下面这样显式捕获全局变量,所以编译会失败:autolambda{[global]{global=2;}};//error所以建议不要使用全局变量。对于不捕获任何内容的lambda表达式,编译器会自动提供将lambda表达式转换为函数指针的转换运算符。这样的lambda表达式可以作为参数传递给其他函数。在C++20中,对lambda表达式进行了一些更新。Lambda表达式可以模板化,lambda表达式可以默认构造、复制和赋值,如下:autolambda{[](inta,intb){returna+b;}};decltype(lambda)lambda2;//默认构造autocopy{lambda};//复制构造copy=lambda2;//复制赋值这不是本文的主题,就不过多介绍了。