什么是 lambda 表达式,何时应该使用?

What is a lambda expression, and when should I use one?

提问人:Nawaz 提问时间:10/2/2011 最后编辑:Jan SchultkeNawaz 更新时间:9/27/2023 访问量:658546

问:

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

一些示例和用例会很有用。

C Lambda ++11 C ++-常见问题

评论

24赞 sergiol 2/13/2017
我见过一个 lambda 非常有用的案例:我的一位同事正在编写具有数百万次迭代的代码来解决空间优化问题。当使用 lambda 时,该算法比使用适当的函数要快得多!编译器是Visual C++ 2013。
1赞 Gabriel Staples 11/27/2021
这是另一个非常好的参考资料,它很好地解释了 C++ 中的 lambda 表达式是什么:Microsoft.com:C++ 中的 lambda 表达式。我特别喜欢它很好地解释了 lambda 表达式的各个部分,特别是:捕获子句、参数列表、尾随返回类型和 lambda 正文

答:

1776赞 Flexo 10/2/2011 #1

问题

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

但是,这是不允许的,不能传递给 C++03 中的模板函数。f

新的解决方案

C++ 11 引入了 lambda,允许您编写一个内联的匿名函子来替换 .对于小的简单示例,这可以更清晰地阅读(它将所有内容保存在一个地方),并且可能更易于维护,例如以最简单的形式: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 中使用其他变量。如果要访问其他变量,可以使用捕获子句(表达式的),到目前为止,这些示例中尚未使用该子句,例如:[]

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, zeta]按引用捕获 epsilon 并按值捕获 zeta
  • [&]通过引用捕获 lambda 中使用的所有变量
  • [=]按值捕获 lambda 中使用的所有变量
  • [&, epsilon]通过引用捕获 Lambda 中使用的所有变量,但按值捕获 epsilon
  • [=, &epsilon]按值捕获 Lambda 中使用的所有变量,但按引用捕获 epsilon

默认情况下,生成的文件意味着捕获将在您默认访问它们时进行。这样做的效果是,具有相同输入的每个调用将产生相同的结果,但是您可以将 lambda 标记为可变,以请求生成的 不是 .operator()constconstoperator()const

评论

10赞 Johannes Schaub - litb 4/1/2013
@Yakk你被困住了。没有捕获的 Lambda 具有到函数类型指针的隐式转换。转换函数始终是...const
3赞 user253751 3/9/2014
有趣 - 我最初认为 lambda 是匿名函数而不是函子,并且对捕获的工作原理感到困惑。
74赞 Evert Heylen 4/11/2015
如果要在程序中使用 lambda 作为变量,可以使用: 但通常,我们让编译器推断出类型: (别忘了std::function<double(int, bool)> f = [](int a, bool b) -> double { ... };auto f = [](int a, bool b) -> double { ... };#include <functional>)
25赞 Lundin 12/17/2015
我想不是每个人都明白为什么当其中一个操作数是整数常量时,保证返回双精度(这是因为 ?: 运算符的隐式提升规则,其中第 2 个和第 3 个操作数通过通常的算术转换相互平衡,无论选择哪一个)。更改为可能会使示例更易于理解。return d < 0.00001 ? 0 : d;0.0 : d
4赞 Flexo 2/26/2018
@MohammadMamunHossain使用数组而不是原始数组,那么它就变得微不足道了。(无论如何,在大多数情况下,在 C++ 中,这是一个很好的建议)std::array
944赞 pmr 10/2/2011 #2

什么是 lambda 函数?

lambda 函数的 C++ 概念起源于 lambda 演算和函数式编程。lambda 是一个未命名的函数,对于无法重用且不值得命名的短代码片段很有用(在实际编程中,而不是理论上)。

在 C++ 中,lambda 函数是这样定义的

[]() { } // barebone lambda

或在其所有的荣耀中

[]() mutable -> T { } // T is the return type, still lacking throw()

[]是捕获列表、参数列表和函数体。(){}

捕获列表

捕获列表定义了 lambda 外部的哪些内容应该在函数体内可用以及如何可用。 它可以是:

  1. 值:[x]
  2. 引用 [&x]
  3. 引用当前范围内的任何变量 [&]
  4. 与 3 相同,但按值 [=]

您可以在逗号分隔的列表中混合使用上述任何内容。[x, &y]

参数列表

参数列表与任何其他 C++ 函数中的参数列表相同。

函数体

实际调用 lambda 时将执行的代码。

退货类型扣除

如果 lambda 只有一个 return 语句,则可以省略返回类型,并具有隐式类型 .decltype(return_statement)

可变

如果 lambda 被标记为可变(例如 ),则允许它改变已由 value 捕获的值。[]() mutable { }

使用案例

由 ISO 标准定义的库从 lambda 中受益匪浅,并将可用性提高了几条,因为现在用户不必在一些可访问的范围内使用小型函子来混淆他们的代码。

C++14

在 C++14 中,lambda 已被各种提案扩展。

初始化的 Lambda 捕获

捕获列表的元素现在可以使用 初始化。这允许重命名变量并通过移动进行捕获。从标准中获取的一个例子:=

int x = 4;
auto y = [&r = x, x = x+1]()->int {
            r += 2;
            return x+2;
         }();  // Updates ::x to 6, and initializes y to 7.

一个摘自维基百科,展示了如何捕获:std::move

auto ptr = std::make_unique<int>(10); // See below for std::make_unique
auto lambda = [ptr = std::move(ptr)] {return *ptr;};

通用 Lambda

Lambda 现在可以是通用的(如果是周围作用域中某处的类型模板参数,则相当于这里):autoTT

auto lambda = [](auto x, auto y) {return x + y;};

改进的退货类型扣除

C++14 允许为每个函数推导返回类型,并且不将其限制为 形式的函数。这也扩展到 lambdas。return expression;

评论

4赞 Ramakrishnan Kannan 6/9/2016
在上面初始化的 lambda 捕获示例中,为什么以 (); 结束 lamba 函数?这看起来像 [](){}();而不是 [](){};。x 的值不应该是 5 吗?
9赞 The Vee 7/14/2016
@RamakrishnanKannan:1) () 在定义 lambda 后立即调用它并给 y 它的返回值。变量 y 是整数,而不是 lambda。2) 不,x=5 是 lambda 的本地值(按值捕获,恰好与外部作用域变量 x 同名),然后返回 x+2 = 5+2。外部变量 x 的重新赋值通过引用 r: 发生,但这发生在原始值 4 上。r = &x; r += 2;
0赞 http8086 12/6/2020
嘿,说,这是什么意思?这意味着全局捕获所有全局变量以及此函数中的任何局部变量?any variable currently in scope
0赞 Sxubach 6/23/2022
我在文档中看到添加了 Throw:learn.microsoft.com/en-us/cpp/cpp/...
0赞 John Doe 10/10/2023
@TheVee 7 年后发表评论,只是想说 lambda 内部和外部不同的例子是卑鄙的。我花了一秒钟才明白为什么会这样:)x
180赞 Brent Bradburn 3/1/2013 #3

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 表达式还允许您创建命名嵌套函数,这是避免重复逻辑的便捷方法。在将一个非平凡的函数作为参数传递给另一个函数时,使用命名 lambda 也往往更容易一些(与匿名内联 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);

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

评论

11赞 Piotr99 3/1/2013
你有没有意识到这个问题是 1.5 年前问的,而上一次活动是将近 1 年前?无论如何,您正在贡献一些我以前从未见过的有趣想法!
8赞 Blacklight Shining 3/2/2013
感谢您同时定义和执行提示!我认为值得注意的是,这可以作为语句的连续体: ,假设是ifif ([i]{ for (char j : i) if (!isspace(j)) return false ; return true ; }()) // i is all whitespaceistd::string
80赞 Brent Bradburn 4/14/2013
因此,以下为法律表述:。[](){}();
2赞 Brent Bradburn 5/2/2014
@MarkLakata:嵌套函数可以在另一个函数声明,如果需要,甚至可以引用外部函数的数据。
9赞 Mark Lakata 5/3/2014
@nobar - 你是对的,我打错了。这是合法的(这次我测试过了)main() {{{{((([](){{}}())));}}}}
18赞 Ted 3/11/2015 #4

lambda 函数是您以内联方式创建的匿名函数。正如一些人所解释的那样,它可以捕获变量(例如 http://www.stroustrup.com/C++11FAQ.html#lambda),但存在一些限制。例如,如果有这样的回调接口,

void apply(void (*f)(int)) {
    f(10);
    f(20);
    f(30);
}

你可以当场写一个函数来使用它,就像下面传递要应用的函数一样:

int col=0;
void output() {
    apply([](int data) {
        cout << data << ((++col % 10) ? ' ' : '\n');
    });
}

但你不能这样做:

void output(int n) {
    int col=0;
    apply([&col,n](int data) {
        cout << data << ((++col % 10) ? ' ' : '\n');
    });
}

由于 C++11 标准中的限制。如果要使用捕获,则必须依赖库和

#include <functional> 

(或其他一些 STL 库,如算法来间接获取它),然后使用 std::function,而不是将普通函数作为参数传递,如下所示:

#include <functional>
void apply(std::function<void(int)> f) {
    f(10);
    f(20);
    f(30);
}
void output(int width) {
    int col;
    apply([width,&col](int data) {
        cout << data << ((++col % width) ? ' ' : '\n');
    });
}

评论

1赞 sp2danny 3/11/2015
原因是,如果 lambda 没有捕获,则只能转换为函数指针。如果是一个接受函子的模板,它将起作用apply
1赞 Ted 3/11/2015
但问题是,如果 apply 是一个现有接口,您可能无法以与普通旧函数不同的方式声明它。该标准可以设计为允许在每次执行此类 lambda 表达式时生成普通旧函数的新实例,并生成对捕获变量的硬编码引用。似乎在编译时生成了一个 lambda 函数。还有其他后果。例如,如果你声明了一个静态变量,即使你重新计算了 lambda 表达式,你也不会得到一个新的静态变量。
1赞 sp2danny 3/11/2015
函数指针通常用于保存,并且 lambda 捕获可能会超出范围。只有无捕获的 lambda 才能转换为函数指针是设计使然
1赞 Ted 3/11/2015
无论哪种方式,您仍然需要注意出于相同原因而释放堆栈变量。请参阅 blogs.msdn.com/b/nativeconcurrency/archive/2012/01/29/...我用 output 和 apply 编写的示例是这样编写的,如果允许和使用函数指针,它们也会起作用。col 将保持分配状态,直到 apply 中的所有函数调用都完成。您将如何重写此代码以使用现有的 apply 接口工作?你最终会使用全局变量还是静态变量,或者一些更晦涩的代码转换?
1赞 Ted 3/11/2015
或者,您可能只是说 lambda 表达式是右值,因此是临时的,但代码保持不变(单例/静态),以便将来可以调用它。在这种情况下,只要函数堆栈分配的捕获保持分配状态,该函数就应该保持分配状态。当然,例如,如果在一个循环中分配函数的许多变体,它可能会变得混乱。
45赞 Konstantin Burlachenko 6/4/2015 #5

答案

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

答:在后台,它是具有重载 operator() const 的自动生成类的对象。这样的对象称为闭包,由编译器创建。 这个“闭包”概念与 C++11 中的绑定概念很接近。 但 lambda 通常会生成更好的代码。通过闭包的调用允许完全内联。

问:我什么时候会使用?

答:定义“简单和小的逻辑”,并要求编译器从上一个问题执行生成。你给编译器一些你想在 operator() 中存在的表达式。编译器将为您生成所有其他内容。

问:他们解决了哪一类问题,而这些问题在引入之前是不可能的?

答:它是某种语法糖,如运算符重载,而不是用于自定义添加、subrtact 操作的函数......但它节省了更多不需要的代码行,将 1-3 行真实逻辑包装到某些类,等等!一些工程师认为,如果行数越少,那么出错的机会就越小(我也这么认为)

使用示例

auto x = [=](int arg1){printf("%i", arg1); };
void(*f)(int) = x;
f(1);
x(1);

关于lambda的额外内容,不在问题中。如果您不感兴趣,请忽略此部分

1. 捕获的值。您可以捕获的内容

1.1. 您可以引用具有静态存储持续时间的变量(以 lambda 为单位)。他们都被俘虏了。

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. 转换

4.1 !!Lambda 不是函数指针,也不是匿名函数,但无捕获 lambda 可以隐式转换为函数指针。

附言

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

评论

1赞 GetFree 4/15/2017
这似乎不是有效的语法。正确的形式是[&,=Param2](int arg1){}[&,Param2](int arg1){}
0赞 Konstantin Burlachenko 4/16/2017
谢谢。首先,我尝试编译此代码段。捕获列表中允许的修饰符似乎很奇怪 // g++ -std=c++11 main.cpp -o test_bin; ./test_bin #include <stdio.h> int main() { #if 1 { int param = 0; auto f=[=,&param](int arg1) mutable {param = arg1;}; f(111); printf(“%i\n”, 参数); } #endif #if 0 { int 参数 = 0; 自动 f=[&,=param](int arg1) 可变 {param = arg1;};f(111);printf(“%i\n”, 参数);} #endif 返回 0;}
0赞 Konstantin Burlachenko 4/16/2017
看起来评论中不支持新行。然后我打开了 5.1.2 Lambda 表达式,第 88 页,“工作草案,编程语言 C++ 标准”,文件编号:#337,2012-01-16。并研究了语法语法。你是对的。不存在通过“=arg”捕获之类的东西
1赞 Konstantin Burlachenko 4/16/2017
非常感谢,在描述中修复了它,并获得了新的知识。
0赞 sergiol 6/27/2015 #6

它解决了一个问题:对于使用输出参数函数初始化 const 成员的构造函数调用,代码比 lambda 更简单

您可以初始化类的 const 成员,调用一个函数,该函数通过将其输出作为输出参数返回来设置其值。

评论

1赞 SirGuy 9/9/2016
这也可以通过一个普通函数来完成,这甚至是你链接到的问题的公认答案所说的。
3赞 Misgevolution 11/23/2015 #7

嗯,我发现的一个实际用途是减少样板代码。例如:

void process_z_vec(vector<int>& vec)
{
  auto print_2d = [](const vector<int>& board, int bsize)
  {
    for(int i = 0; i<bsize; i++)
    {
      for(int j=0; j<bsize; j++)
      {
        cout << board[bsize*i+j] << " ";
      }
      cout << "\n";
    }
  };
  // Do sth with the vec.
  print_2d(vec,x_size);
  // Do sth else with the vec.
  print_2d(vec,y_size);
  //... 
}

如果没有 lambda,您可能需要针对不同的情况执行一些操作。当然,你可以创建一个函数,但是如果你想将使用限制在灵魂用户函数的范围内怎么办?lambda 的性质满足了这一要求,我将其用于这种情况。bsize

评论

1赞 Elliott 8/31/2021
不确定这是否是何时使用 lambda 的最佳示例。这是用老式 C++ 编写相同内容的更简单、更短的方法
16赞 hbk 11/9/2016 #8

C++ Bjarne Stroustrup的作者在他的书第11章(ISBN-13:978-0321563842)中给出了最好的解释之一:lambda expression***The C++ Programming Language***

What is a lambda expression?

lambda 表达式,有时也称为 lambda 函数或(严格来说不正确,但通俗地说)作为 lambda,是用于定义和使用匿名函数对象的简化表示法。而不是使用 operator() 定义一个命名类,然后创建该类的对象,最后 调用它,我们可以使用速记。

When would I use one?

当我们想将操作作为 参数添加到算法中。在图形用户界面的上下文中 (和其他地方),此类操作通常称为回调

What class of problem do they solve that wasn't possible prior to their introduction?

在这里,我想使用 lambda 表达式完成的每个操作都可以在没有它们的情况下解决,但需要更多的代码和更大的复杂性。Lambda 表达式:这是优化代码的方式,也是使其更具吸引力的一种方式。正如斯特劳斯图普所悲伤的那样:

有效的优化方法

Some examples

通过 lambda 表达式

void print_modulo(const vector<int>& v, ostream& os, int m) // output v[i] to os if v[i]%m==0
{
    for_each(begin(v),end(v),
        [&os,m](int x) { 
           if (x%m==0) os << x << '\n';
         });
}

或通过功能

class Modulo_print {
         ostream& os; // members to hold the capture list int m;
     public:
         Modulo_print(ostream& s, int mm) :os(s), m(mm) {} 
         void operator()(int x) const
           { 
             if (x%m==0) os << x << '\n'; 
           }
};

甚至

void print_modulo(const vector<int>& v, ostream& os, int m) 
     // output v[i] to os if v[i]%m==0
{
    class Modulo_print {
        ostream& os; // members to hold the capture list
        int m; 
        public:
           Modulo_print (ostream& s, int mm) :os(s), m(mm) {}
           void operator()(int x) const
           { 
               if (x%m==0) os << x << '\n';
           }
     };
     for_each(begin(v),end(v),Modulo_print{os,m}); 
}

如果需要,您可以像下面这样命名:lambda expression

void print_modulo(const vector<int>& v, ostream& os, int m)
    // output v[i] to os if v[i]%m==0
{
      auto Modulo_print = [&os,m] (int x) { if (x%m==0) os << x << '\n'; };
      for_each(begin(v),end(v),Modulo_print);
 }

或者假设另一个简单的样本

void TestFunctions::simpleLambda() {
    bool sensitive = true;
    std::vector<int> v = std::vector<int>({1,33,3,4,5,6,7});

    sort(v.begin(),v.end(),
         [sensitive](int x, int y) {
             printf("\n%i\n",  x < y);
             return sensitive ? x < y : abs(x) < abs(y);
         });


    printf("sorted");
    for_each(v.begin(), v.end(),
             [](int x) {
                 printf("x - %i;", x);
             }
             );
}

将生成下一个

0

1

0

1

0

1

0

1

0

1

0 排序x - 1;x - 3;x - 4;x - 5;x - 6;x - 7;x - 33;

[]- 这是捕获列表或:如果不需要访问他们的本地环境,我们可以使用它。lambda introducerlambdas

书中的引述:

lambda 表达式的第一个字符始终是 [.lambda 介绍人可以采取多种形式:

[]:空捕获列表。这 意味着不能使用周围上下文中的本地名称 在 lambda 正文中。对于此类 lambda 表达式,数据是从 参数或来自非局部变量。

[&]:隐式捕获 参考。可以使用所有本地名称。所有局部变量都是 通过引用访问。

[=]:按值隐式捕获。所有本地 可以使用名称。所有名称都是指局部变量的副本 在 lambda 表达式的调用点获取。

• [capture-list]:显式捕获;capture-list 是要通过引用或值捕获(即存储在对象中)的局部变量的名称列表。名称以 & 开头的变量由 参考。其他变量按值捕获。捕获列表可以 还包含这个和名称,后跟......作为元素。

• [&, capture-list]:通过引用隐式捕获列表中未提及名称的所有局部变量。捕获列表可以包含此内容。列出的名称前面不能以 & 开头。在 捕获列表按值捕获。

• [=, capture-list]:按值隐式捕获列表中未提及名称的所有局部变量。捕获列表不能包含此内容。列出的名称必须以 & 开头。捕获列表中命名的可变变量通过引用捕获。

请注意,以 & 开头的本地名称始终由 引用和不以 & 开头的本地名称始终由 价值。只有通过引用捕获才允许修改 调用环境。

Additional

Lambda expression格式

enter image description here

其他参考资料:

评论

1赞 Dietrich Baumgarten 10/18/2019
很好的解释。使用基于范围的 for 循环,您可以避免 lambda 并缩短代码for (int x : v) { if (x % m == 0) os << x << '\n';}
0赞 Sam Ginrich 4/11/2022
最后是一个可以抓住的答案,比如“它是什么?”,“用例是什么?”。
6赞 Sachin Nale 6/4/2019 #9

c++ 中的 lambda 被视为“移动可用函数”。 是的,它实际上是在旅途中,你定义它;使用它;随着父函数作用域的完成,lambda 函数也消失了。

C++ 在 C++ 11 中引入了它,每个人都开始在各个可能的地方使用它。 示例和 lambda 可以在此处找到 https://en.cppreference.com/w/cpp/language/lambda

我将描述哪些不存在,但对于每个 C++ 程序员来说,哪些是必不可少的

Lambda 并不打算在任何地方使用,并且每个函数都不能替换为 lambda。与正常功能相比,它也不是最快的。因为它有一些开销,需要由 Lambda 处理。

在某些情况下,它肯定会有助于减少行数。 它基本上可以用于代码部分,该代码部分在同一函数中被调用一次或多次,并且该代码段在其他任何地方都不需要,因此您可以为其创建独立函数。

以下是 lambda 的基本示例以及后台发生的情况。

用户代码:

int main()
{
  // Lambda & auto
  int member=10;
  auto endGame = [=](int a, int b){ return a+b+member;};

  endGame(4,5);

  return 0;

}

编译如何扩展它:

int main()
{
  int member = 10;

  class __lambda_6_18
  {
    int member;
    public: 
    inline /*constexpr */ int operator()(int a, int b) const
    {
      return a + b + member;
    }

    public: __lambda_6_18(int _member)
    : member{_member}
    {}

  };

  __lambda_6_18 endGame = __lambda_6_18{member};
  endGame.operator()(4, 5);

  return 0;
}

所以正如你所看到的,当你使用它时,它会增加什么样的开销。 因此,到处使用它们并不是一个好主意。 它可以在适用的地方使用。

评论

1赞 Nawaz 6/5/2019
是的,它实际上是在旅途中,你定义它;使用它;当父函数作用域完成时,lambda 函数就消失了。如果函数将 Lambda 返回给调用方怎么办?
3赞 Nawaz 6/5/2019
与正常功能相比,它也不是最快的。 因为它有一些开销,需要由 Lambda 处理。你有没有实际运行过任何基准来支持这个说法?相反,lambda + 模板通常会生成尽可能快的代码。
2赞 sun1211 6/29/2021 #10

C++ 11 引入了 lambda 表达式,允许我们编写一个可用于短代码段的内联函数

[ capture clause ] (parameters) -> return-type
{
   definition of method
}

通常,lambda 表达式中的返回类型由编译器本身评估,我们不需要明确指定可以忽略的 -> 返回类型部分,但在一些复杂的情况下,例如在条件语句中,编译器无法确定返回类型,我们需要指定。

// C++ program to demonstrate lambda expression in C++
#include <bits/stdc++.h>
using namespace std;

// Function to print vector
void printVector(vector<int> v)
{
    // lambda expression to print vector
    for_each(v.begin(), v.end(), [](int i)
    {
        std::cout << i << " ";
    });
    cout << endl;
}

int main()
{
    vector<int> v {4, 1, 3, 5, 2, 3, 1, 7};

    printVector(v);

    // below snippet find first number greater than 4
    // find_if searches for an element for which
    // function(third argument) returns true
    vector<int>:: iterator p = find_if(v.begin(), v.end(), [](int i)
    {
        return i > 4;
    });
    cout << "First number greater than 4 is : " << *p << endl;


    // function to sort vector, lambda expression is for sorting in
    // non-decreasing order Compiler can make out return type as
    // bool, but shown here just for explanation
    sort(v.begin(), v.end(), [](const int& a, const int& b) -> bool
    {
        return a > b;
    });

    printVector(v);

    // function to count numbers greater than or equal to 5
    int count_5 = count_if(v.begin(), v.end(), [](int a)
    {
        return (a >= 5);
    });
    cout << "The number of elements greater than or equal to 5 is : "
        << count_5 << endl;

    // function for removing duplicate element (after sorting all
    // duplicate comes together)
    p = unique(v.begin(), v.end(), [](int a, int b)
    {
        return a == b;
    });

    // resizing vector to make size equal to total different number
    v.resize(distance(v.begin(), p));
    printVector(v);

    // accumulate function accumulate the container on the basis of
    // function provided as third argument
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int f = accumulate(arr, arr + 10, 1, [](int i, int j)
    {
        return i * j;
    });

    cout << "Factorial of 10 is : " << f << endl;

    //   We can also access function by storing this into variable
    auto square = [](int i)
    {
        return i * i;
    };

    cout << "Square of 5 is : " << square(5) << endl;
}

输出

4 1 3 5 2 3 1 7
First number greater than 4 is : 5
7 5 4 3 3 2 1 1
The number of elements greater than or equal to 5 is : 2
7 5 4 3 2 1
Factorial of 10 is : 3628800
Square of 5 is : 25

lambda 表达式可以通过访问封闭作用域中的变量来获得比普通函数更多的功能。我们可以通过三种方式从封闭范围捕获外部变量:

  • 按参考捕获
  • 按价值捕获
  • 两者捕获(混合捕获)

用于捕获变量的语法:

  • [&] : 通过引用捕获所有外部变量
  • [=] :按值捕获所有外部变量
  • [a, &b] : 通过值捕获 A,通过引用捕获 B 捕获子句 [ ] 为空的 lambda 只能访问其局部变量。
    #include <bits/stdc++.h>
    using namespace std;
    
    int main()
    {
        vector<int> v1 = {3, 1, 7, 9};
        vector<int> v2 = {10, 2, 7, 16, 9};
    
        // access v1 and v2 by reference
        auto pushinto = [&] (int m)
        {
            v1.push_back(m);
            v2.push_back(m);
        };
    
        // it pushes 20 in both v1 and v2
        pushinto(20);
    
        // access v1 by copy
        [v1]()
        {
            for (auto p = v1.begin(); p != v1.end(); p++)
            {
                cout << *p << " ";
            }
        };
    
        int N = 5;
    
        // below snippet find first number greater than N
        // [N] denotes, can access only N by value
        vector<int>:: iterator p = find_if(v1.begin(), v1.end(), [N](int i)
        {
            return i > N;
        });
    
        cout << "First number greater than 5 is : " << *p << endl;
    
        // function to count numbers greater than or equal to N
        // [=] denotes, can access all variable
        int count_N = count_if(v1.begin(), v1.end(), [=](int a)
        {
            return (a >= N);
        });
    
        cout << "The number of elements greater than or equal to 5 is : "
            << count_N << endl;
    }

输出:

   First number greater than 5 is : 7
   The number of elements greater than or equal to 5 is : 3