4 回答
TA贡献1842条经验 获得超12个赞
问题
C ++包含有用的通用函数,例如std::for_each
和std::transform
,它们非常方便。不幸的是,他们也可以是相当繁琐的使用,特别是如果函子,你想申请是唯一的特定功能。
#include <algorithm>#include <vector>namespace { struct f { void operator()(int) { // do something } };}void func(std::vector<int>& v) { f f; std::for_each(v.begin(), v.end(), f);}
如果你只使用f
一次并且在那个特定的地方,那么写一个全班只是为了做一些微不足道的事情似乎有点过分了。
在C ++ 03中,您可能想要编写类似下面的内容,以保持函数本地:
void func2(std::vector<int>& v) { struct { void operator()(int) { // do something } } f; std::for_each(v.begin(), v.end(), f);}
但是这是不允许的,f
不能传递给C ++ 03中的模板函数。
新的解决方案
C ++ 11引入了lambdas,允许你编写一个内联的匿名函子来替换struct f
。对于小的简单示例,这可以更清晰地阅读(它将所有内容保存在一个地方)并且可能更简单地维护,例如以最简单的形式:
void func3(std::vector<int>& v) { std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });}
Lambda函数只是匿名函子的语法糖。
返回类型
在简单的情况下,lambda的返回类型是为您推导出来的,例如:
void func4(std::vector<double>& v) { std::transform(v.begin(), v.end(), v.begin(), [](double d) { return d < 0.00001 ? 0 : d; } );}
但是当你开始编写更复杂的lambda时,很快就会遇到编译器无法推断出返回类型的情况,例如:
void func4(std::vector<double>& v) { std::transform(v.begin(), v.end(), v.begin(), [](double d) { if (d < 0.0001) { return 0; } else { return d; } });}
要解决此问题,您可以使用以下方法显式指定lambda函数的返回类型-> T
:
void func4(std::vector<double>& v) { std::transform(v.begin(), v.end(), v.begin(), [](double d) -> double { if (d < 0.0001) { return 0; } else { return d; } });}
“捕获”变量
到目前为止,我们还没有使用除了传递给lambda之外的任何东西,但我们也可以在lambda中使用其他变量。如果要访问其他变量,可以使用capture子句([]
表达式),这些子句在这些示例中尚未使用,例如:
void func5(std::vector<double>& v, const double& epsilon) { std::transform(v.begin(), v.end(), v.begin(), [epsilon](double d) -> double { if (d < epsilon) { return 0; } else { return d; } });}
您可以通过引用和值捕获,您可以分别使用&
和指定=
:
[&epsilon]
通过引用捕获[&]
通过引用捕获lambda中使用的所有变量[=]
按值捕获lambda中使用的所有变量[&, epsilon]
捕获变量,如[&],但epsilon值[=, &epsilon]
捕获变量,如[=],但epsilon通过引用
默认情况下生成的operator()
是const
隐式,默认情况下,const
当您访问它们时捕获将是。这具有以下效果:具有相同输入的每个调用将产生相同的结果,但是您可以将lambda标记为mutable
请求operator()
生成的不是const
。
TA贡献1795条经验 获得超7个赞
Lambda表达式通常用于封装算法,以便将它们传递给另一个函数。但是,可以在定义时立即执行lambda:
[&](){ ...your code... }(); // immediately executed lambda expression
在功能上等同于
{ ...your code... } // simple code block
这使得lambda表达式成为重构复杂函数的强大工具。首先将代码段包装在lambda函数中,如上所示。然后可以在每个步骤之后通过中间测试逐渐执行显式参数化的过程。一旦您完全参数化了代码块(如删除所示&
),您可以将代码移动到外部位置并使其成为正常功能。
同样,您可以使用lambda表达式根据算法的结果初始化变量 ...
int a = []( int b ){ int r=1; while (b>0) r*=b--; return r; }(5); // 5!
作为一种分区程序逻辑的方法,您甚至可能会发现将lambda表达式作为参数传递给另一个lambda表达式很有用......
[&]( std::function<void()> algorithm ) // wrapper section { ...your wrapper code... algorithm(); ...your wrapper code... }([&]() // algorithm section { ...your algorithm code... });
Lambda表达式还允许您创建命名嵌套函数,这可以是避免重复逻辑的便捷方法。当将非平凡函数作为参数传递给另一个函数时,使用命名的lambdas在眼睛上也会更容易(与匿名内联lambda相比)。 注意:关闭大括号后不要忘记分号。
auto algorithm = [&]( double x, double m, double b ) -> double { return m*x+b; };int a=algorithm(1,2,3), b=algorithm(4,5,6);
如果后续分析显示函数对象的显着初始化开销,您可以选择将其重写为普通函数。
TA贡献1797条经验 获得超6个赞
答案
问:C ++ 11中的lambda表达式是什么?
答:在引擎盖下,它是一个带有重载operator()const的自动生成类的对象。这种对象称为闭包,由编译器创建。这个'闭包'概念接近于C ++ 11中的绑定概念。但是lambdas通常会生成更好的代码。通过闭包调用允许完全内联。
问:我什么时候使用?
答:定义“简单和小逻辑”并要求编译器执行上一个问题的生成。你给编译器一些你想要在operator()中的表达式。编译器将为您生成所有其他东西。
问:他们解决了哪些问题在引入之前是不可能的?
答:这是某种语法糖,比如运算符重载而不是自定义添加,子作用操作的函数......但它保存了更多不需要的代码行,将1-3行真实逻辑包装到某些类等等!一些工程师认为,如果线的数量较少,那么在其中产生错误的机会就会减少(我也这么认为)
用法示例
auto x = [=](int arg1){printf("%i", arg1); };void(*f)(int) = x;f(1);x(1);
关于lambdas的额外内容,未提及问题。如果您不感兴趣,请忽略此部分
1.捕获的价值。你可以捕获什么
1.1。您可以在lambdas中引用具有静态存储持续时间的变量。他们都被抓获了。
1.2。您可以使用lambda“按值”捕获值。在这种情况下,捕获的变量将被复制到函数对象(闭包)。
[captureVar1,captureVar2](int arg1){}
1.3。你可以捕获参考。& - 在这种情况下意味着参考,而不是指针。
[&captureVar1,&captureVar2](int arg1){}
1.4。它存在通过值或引用捕获所有非静态变量的符号
[=](int arg1){} // capture all not-static vars by value [&](int arg1){} // capture all not-static vars by reference
1.5。它存在通过值或通过引用捕获所有非静态变量并指定smth的表示法。更多。示例:按值捕获所有非静态变量,但通过引用捕获Param2
[=,&Param2](int arg1){}
通过引用捕获所有非静态变量,但通过值捕获Param2
[&,Param2](int arg1){}
2.退货类型扣除
2.1。如果lambda是一个表达式,则可以推导出Lambda返回类型。或者您可以明确指定它。
[=](int arg1)->trailing_return_type{return trailing_return_type();}
如果lambda有多个表达式,则必须通过尾随返回类型指定返回类型。此外,类似的语法可以应用于自动函数和成员函数
3.捕获的值。什么你无法捕捉
3.1。您只能捕获本地变量,而不能捕获对象的成员变量。
4.Сonversions
4.1 !! Lambda不是函数指针,它不是匿名函数,但可以将无捕获的 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);};
- 4 回答
- 0 关注
- 1262 浏览
添加回答
举报