为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C++
撒科打诨 2019-05-24 15:46:28
什么是C ++ 11中的lambda表达式?什么是C ++ 11中的lambda表达式?我什么时候用?他们解决了哪些问题在引入之前是不可能的?一些示例和用例将是有用的。
查看完整描述

4 回答

?
红颜莎娜

TA贡献1842条经验 获得超12个赞

问题

C ++包含有用的通用函数,例如std::for_eachstd::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


查看完整回答
反对 回复 2019-05-24
?
一只萌萌小番薯

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);

如果后续分析显示函数对象的显着初始化开销,您可以选择将其重写为普通函数。


查看完整回答
反对 回复 2019-05-24
?
互换的青春

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

  1. 有关lambda语法信息的更多信息,请参阅编程语言C ++#337,2012-01-16,5.1.2的工作草案。Lambda表达式,第88页

  2. 在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);};


查看完整回答
反对 回复 2019-05-24
  • 4 回答
  • 0 关注
  • 1218 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信