什么是C ++ 11中的lambda表达式?

什么是C ++ 11中的lambda表达式?

什么是C ++ 11中的lambda表达式?我什么时候用?他们解决了哪些问题在引入之前是不可能的?

一些示例和用例将是有用的。


撒科打诨
浏览 1221回答 4
4回答

红颜莎娜

问题C ++包含有用的通用函数,例如std::for_each和std::transform,它们非常方便。不幸的是,他们也可以是相当繁琐的使用,特别是如果函子,你想申请是唯一的特定功能。#include&nbsp;<algorithm>#include&nbsp;<vector>namespace&nbsp;{ &nbsp;&nbsp;struct&nbsp;f&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;operator()(int)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;do&nbsp;something &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;};}void&nbsp;func(std::vector<int>&&nbsp;v)&nbsp;{ &nbsp;&nbsp;f&nbsp;f; &nbsp;&nbsp;std::for_each(v.begin(),&nbsp;v.end(),&nbsp;f);}如果你只使用f一次并且在那个特定的地方,那么写一个全班只是为了做一些微不足道的事情似乎有点过分了。在C ++ 03中,您可能想要编写类似下面的内容,以保持函数本地:void&nbsp;func2(std::vector<int>&&nbsp;v)&nbsp;{ &nbsp;&nbsp;struct&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;operator()(int)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;do&nbsp;something &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;}&nbsp;f; &nbsp;&nbsp;std::for_each(v.begin(),&nbsp;v.end(),&nbsp;f);}但是这是不允许的,f不能传递给C ++ 03中的模板函数。新的解决方案C ++ 11引入了lambdas,允许你编写一个内联的匿名函子来替换struct f。对于小的简单示例,这可以更清晰地阅读(它将所有内容保存在一个地方)并且可能更简单地维护,例如以最简单的形式:void&nbsp;func3(std::vector<int>&&nbsp;v)&nbsp;{ &nbsp;&nbsp;std::for_each(v.begin(),&nbsp;v.end(),&nbsp;[](int)&nbsp;{&nbsp;/*&nbsp;do&nbsp;something&nbsp;here*/&nbsp;});}Lambda函数只是匿名函子的语法糖。返回类型在简单的情况下,lambda的返回类型是为您推导出来的,例如:void&nbsp;func4(std::vector<double>&&nbsp;v)&nbsp;{ &nbsp;&nbsp;std::transform(v.begin(),&nbsp;v.end(),&nbsp;v.begin(), &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[](double&nbsp;d)&nbsp;{&nbsp;return&nbsp;d&nbsp;<&nbsp;0.00001&nbsp;?&nbsp;0&nbsp;:&nbsp;d;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);}但是当你开始编写更复杂的lambda时,很快就会遇到编译器无法推断出返回类型的情况,例如:void&nbsp;func4(std::vector<double>&&nbsp;v)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;std::transform(v.begin(),&nbsp;v.end(),&nbsp;v.begin(), &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[](double&nbsp;d)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(d&nbsp;<&nbsp;0.0001)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;d; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});}要解决此问题,您可以使用以下方法显式指定lambda函数的返回类型-> T:void&nbsp;func4(std::vector<double>&&nbsp;v)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;std::transform(v.begin(),&nbsp;v.end(),&nbsp;v.begin(), &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[](double&nbsp;d)&nbsp;->&nbsp;double&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(d&nbsp;<&nbsp;0.0001)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;d; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});}“捕获”变量到目前为止,我们还没有使用除了传递给lambda之外的任何东西,但我们也可以在lambda中使用其他变量。如果要访问其他变量,可以使用capture子句([]表达式),这些子句在这些示例中尚未使用,例如:void&nbsp;func5(std::vector<double>&&nbsp;v,&nbsp;const&nbsp;double&&nbsp;epsilon)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;std::transform(v.begin(),&nbsp;v.end(),&nbsp;v.begin(), &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[epsilon](double&nbsp;d)&nbsp;->&nbsp;double&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(d&nbsp;<&nbsp;epsilon)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;d; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});}您可以通过引用和值捕获,您可以分别使用&和指定=:[&epsilon]&nbsp;通过引用捕获[&]&nbsp;通过引用捕获lambda中使用的所有变量[=]&nbsp;按值捕获lambda中使用的所有变量[&, epsilon]&nbsp;捕获变量,如[&],但epsilon值[=, &epsilon]&nbsp;捕获变量,如[=],但epsilon通过引用默认情况下生成的operator()是const隐式,默认情况下,const当您访问它们时捕获将是。这具有以下效果:具有相同输入的每个调用将产生相同的结果,但是您可以将lambda标记为mutable请求operator()生成的不是const。

一只萌萌小番薯

Lambda表达式通常用于封装算法,以便将它们传递给另一个函数。但是,可以在定义时立即执行lambda:[&](){&nbsp;...your&nbsp;code...&nbsp;}();&nbsp;//&nbsp;immediately&nbsp;executed&nbsp;lambda&nbsp;expression在功能上等同于{&nbsp;...your&nbsp;code...&nbsp;}&nbsp;//&nbsp;simple&nbsp;code&nbsp;block这使得lambda表达式成为重构复杂函数的强大工具。首先将代码段包装在lambda函数中,如上所示。然后可以在每个步骤之后通过中间测试逐渐执行显式参数化的过程。一旦您完全参数化了代码块(如删除所示&),您可以将代码移动到外部位置并使其成为正常功能。同样,您可以使用lambda表达式根据算法的结果初始化变量&nbsp;...int&nbsp;a&nbsp;=&nbsp;[](&nbsp;int&nbsp;b&nbsp;){&nbsp;int&nbsp;r=1;&nbsp;while&nbsp;(b>0)&nbsp;r*=b--;&nbsp;return&nbsp;r;&nbsp;}(5);&nbsp;//&nbsp;5!作为一种分区程序逻辑的方法,您甚至可能会发现将lambda表达式作为参数传递给另一个lambda表达式很有用......[&](&nbsp;std::function<void()>&nbsp;algorithm&nbsp;)&nbsp;//&nbsp;wrapper&nbsp;section &nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;...your&nbsp;wrapper&nbsp;code... &nbsp;&nbsp;&nbsp;algorithm(); &nbsp;&nbsp;&nbsp;...your&nbsp;wrapper&nbsp;code... &nbsp;&nbsp;&nbsp;}([&]()&nbsp;//&nbsp;algorithm&nbsp;section &nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;...your&nbsp;algorithm&nbsp;code... &nbsp;&nbsp;&nbsp;});Lambda表达式还允许您创建命名嵌套函数,这可以是避免重复逻辑的便捷方法。当将非平凡函数作为参数传递给另一个函数时,使用命名的lambdas在眼睛上也会更容易(与匿名内联lambda相比)。&nbsp;注意:关闭大括号后不要忘记分号。auto&nbsp;algorithm&nbsp;=&nbsp;[&](&nbsp;double&nbsp;x,&nbsp;double&nbsp;m,&nbsp;double&nbsp;b&nbsp;)&nbsp;->&nbsp;double &nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;return&nbsp;m*x+b; &nbsp;&nbsp;&nbsp;};int&nbsp;a=algorithm(1,2,3),&nbsp;b=algorithm(4,5,6);如果后续分析显示函数对象的显着初始化开销,您可以选择将其重写为普通函数。

互换的青春

答案问:C ++ 11中的lambda表达式是什么?答:在引擎盖下,它是一个带有重载operator()const的自动生成类的对象。这种对象称为闭包,由编译器创建。这个'闭包'概念接近于C ++ 11中的绑定概念。但是lambdas通常会生成更好的代码。通过闭包调用允许完全内联。问:我什么时候使用?答:定义“简单和小逻辑”并要求编译器执行上一个问题的生成。你给编译器一些你想要在operator()中的表达式。编译器将为您生成所有其他东西。问:他们解决了哪些问题在引入之前是不可能的?答:这是某种语法糖,比如运算符重载而不是自定义添加,子作用操作的函数......但它保存了更多不需要的代码行,将1-3行真实逻辑包装到某些类等等!一些工程师认为,如果线的数量较少,那么在其中产生错误的机会就会减少(我也这么认为)用法示例auto&nbsp;x&nbsp;=&nbsp;[=](int&nbsp;arg1){printf("%i",&nbsp;arg1);&nbsp;};void(*f)(int)&nbsp;=&nbsp;x;f(1);x(1);关于lambdas的额外内容,未提及问题。如果您不感兴趣,请忽略此部分1.捕获的价值。你可以捕获什么1.1。您可以在lambdas中引用具有静态存储持续时间的变量。他们都被抓获了。1.2。您可以使用lambda“按值”捕获值。在这种情况下,捕获的变量将被复制到函数对象(闭包)。[captureVar1,captureVar2](int&nbsp;arg1){}1.3。你可以捕获参考。& - 在这种情况下意味着参考,而不是指针。&nbsp;&nbsp;&nbsp;[&captureVar1,&captureVar2](int&nbsp;arg1){}1.4。它存在通过值或引用捕获所有非静态变量的符号&nbsp;&nbsp;[=](int&nbsp;arg1){}&nbsp;//&nbsp;capture&nbsp;all&nbsp;not-static&nbsp;vars&nbsp;by&nbsp;value &nbsp;&nbsp;[&](int&nbsp;arg1){}&nbsp;//&nbsp;capture&nbsp;all&nbsp;not-static&nbsp;vars&nbsp;by&nbsp;reference1.5。它存在通过值或通过引用捕获所有非静态变量并指定smth的表示法。更多。示例:按值捕获所有非静态变量,但通过引用捕获Param2[=,&Param2](int&nbsp;arg1){}通过引用捕获所有非静态变量,但通过值捕获Param2[&,Param2](int&nbsp;arg1){}2.退货类型扣除2.1。如果lambda是一个表达式,则可以推导出Lambda返回类型。或者您可以明确指定它。[=](int&nbsp;arg1)->trailing_return_type{return&nbsp;trailing_return_type();}如果lambda有多个表达式,则必须通过尾随返回类型指定返回类型。此外,类似的语法可以应用于自动函数和成员函数3.捕获的值。什么你无法捕捉3.1。您只能捕获本地变量,而不能捕获对象的成员变量。4.Сonversions4.1 !!&nbsp;Lambda不是函数指针,它不是匿名函数,但可以将无捕获的&nbsp;lambdas隐式转换为函数指针。PS有关lambda语法信息的更多信息,请参阅编程语言C ++#337,2012-01-16,5.1.2的工作草案。Lambda表达式,第88页在C ++ 14中,添加了名为“init capture”的额外功能。它允许对闭包数据成员进行仲裁声明:auto toFloat = [](int value) { return float(value);};auto interpolate = [min = toFloat(0), max = toFloat(255)](int value)->float { return (value - min) / (max - min);};
打开App,查看更多内容
随时随地看视频慕课网APP