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

发布于 2024-12-07 15:39:55 字数 82 浏览 1 评论 0 原文

C++11 中的 lambda 表达式是什么?我什么时候会使用一个?他们解决了哪些在引入之前无法解决的问题?

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

What is a lambda expression in C++11? When would I use one? What class of problem do they solve that wasn't possible prior to their introduction?

A few examples, and use cases would be useful.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

新一帅帅 2024-12-14 15:39:56

解答

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

答:在底层,它是一个自动生成的类的对象,带有重载的operator() const。这样的对象称为闭包,由编译器创建。
这种“闭包”概念类似于 C++11 中的“绑定”概念,但 lambda 通常会生成性能更高的代码。此外,通过闭包(而不是典型函数)的调用允许完全内联。

问:我什么时候会使用一个?

答:当你想定义“简单而小的逻辑”并要求编译器根据上一个问题执行生成时。您为编译器提供了一些您希望位于 operator() 内的表达式。所有其他的东西,编译器都会为你生成。

问: lambda 解决了哪些在引入之前无法解决的问题?

答:Lambda 更像是一种语法糖(即易于使用的代码),例如“运算符重载”,而不必为自定义加、减操作创建整个函数... Lambda节省更多行不需要的代码,将 1-3 行实际逻辑包装到某些类中,等等!一些工程师认为如果行数越少,出错的机会就越小(我也这么认为)。

使用示例

auto x = [=](int arg1){printf("%i", arg1); };
// Note the ending semicolon after {}.

void(*f)(int) = x;
// ^Create function pointer f that takes parameter `int`.
// ^See point 4.1 below under "Extras about Lambdas".

f(1);                // Call function f with parameter `int 1`
x(1);                // Call function x with parameter `int 1`

有关 lambda 的附加内容,问题未涵盖。如果您不感兴趣,请忽略此部分

1.捕获的值。您可以捕获什么

1.1。您可以在 lambda 中引用具有static 存储持续时间的变量。他们都被俘虏了。

1.2.您可以使用 lambda “按值”捕获值。在这种情况下,捕获的变量将被复制到函数对象(闭包)。

[captureVar1,captureVar2](int arg1){}
//Modification of either captured value inside the Lambda
//will *not* modify the value outside the Lambda too,
//meaning a variable outside the Lambda that is also named
//captureVar1 will be unaffected by what happens inside the Lambda.
//I.e., Variable Shadowing will occur.

1.3.您可以通过引用捕获。 & -- 在这种情况下意味着引用,而不是指针。

   [&captureVar1,&captureVar2](int arg1){}
   //Modification of either capture value inside the Lambda
   //will modify the value outside the Lambda too!

1.4.存在符号可以按值或参考

  [=](int arg1){} // capture all not-static vars by value

  [&](int arg1){} // capture all not-static vars by reference

1.5 捕获所有非静态变量。存在符号可以按值或按引用捕获所有非静态变量,同时将特定变量重写为专门按值或按引用
示例:
按值捕获所有非静态变量,但按引用捕获 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 可以隐式转换为函数指针。

Ps

  1. 更多关于 lambda 语法的信息可以在Programming Language C++ #337, 2012-01-16, 5.1.2 中找到。 Lambda 表达式,第 88 页

  2. 在 C++14 中,添加了一个名为“init capture”的额外功能。它允许您任意执行闭包数据成员的声明:

     auto toFloat = [](int value) { return float(value);};
    
     自动插值 = [最小值 = toFloat(0), 最大值 = toFloat(255)]
       (int值)->浮点型
       { return (值 - 最小值) / (最大值 - 最小值);};
    

Answers

Q: What is a lambda expression in C++11?

A: Under the hood, it is the object of an autogenerated class with an overloaded operator() const. Such an object is called a closure and is created by the compiler.
This 'closure' concept is similar to the 'bind' concept from C++11, but lambdas typically generate more performant code. Also, calls through closures (instead of typical functions) allow full inlining.

Q: When would I use one?

A: When you want to define "simple and small logic" and ask the compiler to perform generation from previous question. You give a compiler some expressions which you want to be inside operator(). All the other stuff, the compiler will generate for you.

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

A: Lambdas are more of a syntactical sugar (i.e., code meant for ease-of-use) like "operator overloading" instead of having to make entire functions for custom add, subtract operations... Lambdas save more lines of unneeded code to wrap 1-3 lines of real logic to some classes, and etc.! Some engineers think that if the number of lines is smaller, then there is a lesser chance to make errors in it (I'm think so too).

Example of usage

auto x = [=](int arg1){printf("%i", arg1); };
// Note the ending semicolon after {}.

void(*f)(int) = x;
// ^Create function pointer f that takes parameter `int`.
// ^See point 4.1 below under "Extras about Lambdas".

f(1);                // Call function f with parameter `int 1`
x(1);                // Call function x with parameter `int 1`

Extras about lambdas, not covered by question. Ignore this section if you're not interested

1. Captured values. What you can capture

1.1. You can reference to a variable with static storage duration in lambdas. They all are captured.

1.2. You can use lambda to capture values "by value". In such case, captured vars will be copied to the function object (closure).

[captureVar1,captureVar2](int arg1){}
//Modification of either captured value inside the Lambda
//will *not* modify the value outside the Lambda too,
//meaning a variable outside the Lambda that is also named
//captureVar1 will be unaffected by what happens inside the Lambda.
//I.e., Variable Shadowing will occur.

1.3. You can capture by reference. & -- in this context mean reference, not pointers.

   [&captureVar1,&captureVar2](int arg1){}
   //Modification of either capture value inside the Lambda
   //will modify the value outside the Lambda too!

1.4. Notation exists to capture all non-static vars by value, or by reference

  [=](int arg1){} // capture all not-static vars by value

  [&](int arg1){} // capture all not-static vars by reference

1.5. Notation exists to capture all non-static vars by value, or by reference, while overriding specific variables to be specifically by-value or by-reference
Examples:
Capture all not-static vars by value, but by reference capture Param2

[=,&Param2](int arg1){} 

Capture all not-static vars by reference, but by value capture Param2

[&,Param2](int arg1){} 

2. Return type deduction

2.1. Lambda return type can be deduced if lambda is one expression. Or you can explicitly specify it.

[=](int arg1)->trailing_return_type{return trailing_return_type();}

If lambda has more than one expression, then return type must be specified via trailing return type.
Also, similar syntax can be applied to auto functions and member-functions

3. Captured values. What you cannot capture

3.1. You can capture only local vars, not member variables of the object.

4. Сonversions

4.1 !! Lambda is not a function pointer and it is not an anonymous function, but capture-less lambdas can be implicitly converted to a function pointer.

P.s.

  1. More about lambda grammar information can be found in Working draft for Programming Language C++ #337, 2012-01-16, 5.1.2. Lambda Expressions, p.88

  2. In C++14 an extra feature, named as "init capture", has been added. It allows you to arbitrarily perform declaration of closure data members:

     auto toFloat = [](int value) { return float(value);};
    
     auto interpolate = [min = toFloat(0), max = toFloat(255)]
       (int value)->float
       { return (value - min) / (max - min);};
    
临风闻羌笛 2024-12-14 15:39:56

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

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

您可以当场编写一个函数来使用它,就像下面传递给 apply 的函数一样:

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

A lambda function is an anonymous function that you create in-line. It can capture variables as some have explained, (e.g. http://www.stroustrup.com/C++11FAQ.html#lambda) but there are some limitations. For example, if there's a callback interface like this,

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

you can write a function on the spot to use it like the one passed to apply below:

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

But you can't do this:

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

because of limitations in the C++11 standard. If you want to use captures, you have to rely on the library and

#include <functional> 

(or some other STL library like algorithm to get it indirectly) and then work with std::function instead of passing normal functions as parameters like this:

#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');
    });
}
绮筵 2024-12-14 15:39:56

C++ 作者 Bjarne Stroustrup 在他的书 ***C++ 编程语言*** 中给出了 lambda 表达式 的最佳解释之一。第 11 章 (ISBN-13: 978-0321563842):

什么是 lambda 表达式?

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

我什么时候会使用它?

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

他们解决了什么类型的问题,而这些问题在引入之前是不可能的?

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

有效的优化方法

优化一些示例

通过 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 表达式,如下所示:

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 引入器:如果 lambda 不需要访问其本地环境,我们可以使用它。

引自书中:

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

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

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

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

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

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

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

请注意,本地名称前面带有 &总是被捕获
引用和本地名称前面不加 &总是被捕获
价值。仅通过引用捕获才允许修改中的变量
调用环境。

其他

Lambda表达式格式

在此处输入图像描述

其他参考:

One of the best explanation of lambda expression is given from author of C++ Bjarne Stroustrup in his book ***The C++ Programming Language*** chapter 11 (ISBN-13: 978-0321563842):

What is a lambda expression?

A lambda expression, sometimes also referred to as a lambda
function or (strictly speaking incorrectly, but colloquially) as a
lambda, is a simplified notation for defining and using an anonymous function object. Instead of defining a named class with an operator(), later making an object of that class, and finally
invoking it, we can use a shorthand.

When would I use one?

This is particularly useful when we want to pass an operation as an
argument to an algorithm. In the context of graphical user interfaces
(and elsewhere), such operations are often referred to as callbacks.

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

Here i guess every action done with lambda expression can be solved without them, but with much more code and much bigger complexity. Lambda expression this is the way of optimization for your code and a way of making it more attractive. As sad by Stroustup :

effective ways of optimizing

Some examples

via lambda expression

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

or via function

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

or even

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

if u need u can name lambda expression like below:

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

Or assume another simple sample

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

will generate next

0

1

0

1

0

1

0

1

0

1

0 sortedx - 1;x - 3;x - 4;x - 5;x - 6;x - 7;x - 33;

[] - this is capture list or lambda introducer: if lambdas require no access to their local environment we can use it.

Quote from book:

The first character of a lambda expression is always [. A lambda
introducer can take various forms:

[]: an empty capture list. This
implies that no local names from the surrounding context can be used
in the lambda body. For such lambda expressions, data is obtained from
arguments or from nonlocal variables.

[&]: implicitly capture by
reference. All local names can be used. All local variables are
accessed by reference.

[=]: implicitly capture by value. All local
names can be used. All names refer to copies of the local variables
taken at the point of call of the lambda expression.

[capture-list]: explicit capture; the capture-list is the list of names of local variables to be captured (i.e., stored in the object) by reference or by value. Variables with names preceded by & are captured by
reference. Other variables are captured by value. A capture list can
also contain this and names followed by ... as elements.

[&, capture-list]: implicitly capture by reference all local variables with names not men- tioned in the list. The capture list can contain this. Listed names cannot be preceded by &. Variables named in the
capture list are captured by value.

[=, capture-list]: implicitly capture by value all local variables with names not mentioned in the list. The capture list cannot contain this. The listed names must be preceded by &. Vari- ables named in the capture list are captured by reference.

Note that a local name preceded by & is always captured by
reference and a local name not pre- ceded by & is always captured by
value. Only capture by reference allows modification of variables in
the calling environment.

Additional

Lambda expression format

enter image description here

Additional references:

假面具 2024-12-14 15:39:56

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

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

The lambda's in c++ are treated as "on the go available function".
yes its literally on the go, you define it; use it; and as the parent function scope finishes the lambda function is gone.

c++ introduced it in c++ 11 and everyone started using it like at every possible place.
the example and what is lambda can be find here https://en.cppreference.com/w/cpp/language/lambda

i will describe which is not there but essential to know for every c++ programmer

Lambda is not meant to use everywhere and every function cannot be replaced with lambda. It's also not the fastest one compare to normal function. because it has some overhead which need to be handled by lambda.

it will surely help in reducing number of lines in some cases.
it can be basically used for the section of code, which is getting called in same function one or more time and that piece of code is not needed anywhere else so that you can create standalone function for it.

Below is the basic example of lambda and what happens in background.

User code:

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

  endGame(4,5);

  return 0;

}

How compile expands it:

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

so as you can see, what kind of overhead it adds when you use it.
so its not good idea to use them everywhere.
it can be used at places where they are applicable.

高速公鹿 2024-12-14 15:39:56

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

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

Well, one practical use I've found out is reducing boiler plate code. For example:

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

Without lambda, you may need to do something for different bsize cases. Of course you could create a function but what if you want to limit the usage within the scope of the soul user function? the nature of lambda fulfills this requirement and I use it for that case.

转角预定愛 2024-12-14 15:39:56

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

C++ 11 introduced lambda expression to allow us write an inline function which can be used for short snippets of code

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

Generally return-type in lambda expression are evaluated by compiler itself and we don’t need to specify that explicitly and -> return-type part can be ignored but in some complex case as in conditional statement, compiler can’t make out the return type and we need to specify that.

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

Output

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

A lambda expression can have more power than an ordinary function by having access to variables from the enclosing scope. We can capture external variables from enclosing scope by three ways :

  • Capture by reference
  • Capture by value
  • Capture by both (mixed capture)

The syntax used for capturing variables :

  • [&] : capture all external variable by reference
  • [=] : capture all external variable by value
  • [a, &b] : capture a by value and b by reference
    A lambda with empty capture clause [ ] can access only those variable which are local to it.
    #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;
    }

Output:

   First number greater than 5 is : 7
   The number of elements greater than or equal to 5 is : 3
阿楠 2024-12-14 15:39:56

对于初学者来说,之前(及时)给出的答案仍然不够清晰。特别是因为 lambda 可以定义/调用的形式不同。好吧,也许另一个初学者的回答(没有“知识的诅咒”)可能有助于澄清这些不同的形式。

#include <iostream>
int main()
{
    //                              lambda| arguments  | |   body      ||invoc.|
    printf("direct lamda call: %d\n",  [] (int x, int y) {return x + y;} (4, 5) );
    // it is important to be aware that a lambda with invocation arguments
    // or () if no arguments are defined, can be inserted anywhere where a
    // value (whatever type) is expected

    // assignment (instead of "auto", one could also write "int"):
    auto retval = [] (int x, int y) {return x + y;}(5, 5);
    printf("the same, via variable: %d\n", retval);
    // now the same, without supplying actual arguments (definition only):
    auto funct  = [] (int x, int y) {return x + y;};
    printf("function address: %x\n", funct);
    printf("via named lambda invocation: %d\n", funct(6, 5) );
    // in this case (definition only), the lambda can be handed over to another
    // function as well, e.g.
}

输出:(

$ g++ lambda.cpp && ./a.out
direct lamda call: 9
the same, via variable: 10
function address: 4878deb0
via named lambda invocation: 11

灵感来自 https://dotnettutorials.net/lesson/lambda-expressions -in-cpp/

为了解决问题发布者的进一步问题,显示了一个演示,其中将数据与 lambda 一起用作数据处理器函数 à la map() 中的函数参数:

#include <iostream>
#include <vector>
using namespace std;
template<typename Function>
void mymap(vector<int> a, Function fn) {
    for (auto &i : a) {
        printf("%d>%d  ", i, fn(a[i-1]));
    }
    printf("\n");
}
int main()
{
    vector<int> data{1,2,3,4,5};
    //   |what|       how              |
    mymap(data, [=](int x){return x*x;});
}

输出:

$ g++ lambda.cpp && ./a.out
1>1  2>4  3>9  4>16  5>25  

有关的更多信息这减少代码重复的典型用例可以从堆栈溢出 (2006) 的创始人之一那里找到:
https://www.joelonsoftware.com /2006/08/01/can-your-programming-language-do-this/
顺便说一句:您可以将 lambda 表达式想象为在函数中定义了普通(数学)表达式,可以在另一个函数中使用(计算)这些表达式。 lambda 有效地实现了这一点,甚至更多。

Lambda 和其他技术还有助于在进行代码更改时减少 git diff 的“大小”。引用 Martin Cracauer 在他的介绍中编译时计算

最后但同样重要的一点是,还有差异效率。什么是
到底是“差异效率”?这意味着如果你查看 git diff
您的代码与代码的不同状态,那么您只能看到
实际的功能变化。没有很多样板。副本不多
并粘贴了该功能更改的轻微变体。

(我知道文章的标题不是关于 lambda 的,但是围绕文章的中心,可以找到一些通用的有用注释)

The answers given before (in time) are still missing some clarity for the beginner. Especially because of the different forms that a lambda can be defined/invoked. Well, perhaps an answer from another beginner (no 'curse of knowledge') may help clarify these different forms.

#include <iostream>
int main()
{
    //                              lambda| arguments  | |   body      ||invoc.|
    printf("direct lamda call: %d\n",  [] (int x, int y) {return x + y;} (4, 5) );
    // it is important to be aware that a lambda with invocation arguments
    // or () if no arguments are defined, can be inserted anywhere where a
    // value (whatever type) is expected

    // assignment (instead of "auto", one could also write "int"):
    auto retval = [] (int x, int y) {return x + y;}(5, 5);
    printf("the same, via variable: %d\n", retval);
    // now the same, without supplying actual arguments (definition only):
    auto funct  = [] (int x, int y) {return x + y;};
    printf("function address: %x\n", funct);
    printf("via named lambda invocation: %d\n", funct(6, 5) );
    // in this case (definition only), the lambda can be handed over to another
    // function as well, e.g.
}

Output:

$ g++ lambda.cpp && ./a.out
direct lamda call: 9
the same, via variable: 10
function address: 4878deb0
via named lambda invocation: 11

(inspiration got via https://dotnettutorials.net/lesson/lambda-expressions-in-cpp/)

To address the further questions of the question poster, a demonstration is shown where data is used together with lambda as function arguments in a data processor function à la map():

#include <iostream>
#include <vector>
using namespace std;
template<typename Function>
void mymap(vector<int> a, Function fn) {
    for (auto &i : a) {
        printf("%d>%d  ", i, fn(a[i-1]));
    }
    printf("\n");
}
int main()
{
    vector<int> data{1,2,3,4,5};
    //   |what|       how              |
    mymap(data, [=](int x){return x*x;});
}

Output:

$ g++ lambda.cpp && ./a.out
1>1  2>4  3>9  4>16  5>25  

More information about this typical use case to reduce code repetitions can be found here from one of the founders of stack overflow (2006):
https://www.joelonsoftware.com/2006/08/01/can-your-programming-language-do-this/.
BTW: you could think of a lambda expression as if we had normal (mathematical) expressions defined in a function, where these can be used (computed) from within another function. The lambda effectively allows this and even more.

Lambdas and other techniques also help reduce git diff 'size' upon doing code changes. To quote Martin Cracauer in his introduction to Compile Time Computing:

Last but certainly not least, there is also Diff Efficiency. What the
heck is “diff efficiency”? It means that if you look at a git diff of
your code versus a different state of your code, then you only see the
actual functional change. Not a lot of boilerplate. Not a lot of copy
and pasted slight variants of that functional change.

(I know that the title of the article is not about lambdas, but around the center of the article, some general useful notes can be found)

夜司空 2024-12-14 15:39:56

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

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

One problem it solves: Code simpler than lambda for a call in constructor that uses an output parameter function for initializing a const member

You can initialize a const member of your class, with a call to a function that sets its value by giving back its output as an output parameter.

耳钉梦 2024-12-14 15:39:55

问题

C++ 包含有用的通用函数,例如 std::for_each 和 std::transform ,它们非常方便。不幸的是,它们使用起来也很麻烦,特别是如果您想要apply 对于特定函数来说是唯一的。

#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 引入了 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 中使用其他变量。如果您想访问其他变量,您可以使用 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, zeta] 通过引用捕获 epsilon,通过值捕获 zeta
  • [&] 捕获所有变量在 lambda 中按引用使用
  • [=] 通过值捕获 lambda 中使用的所有变量
  • [&, epsilon] 通过引用捕获 lambda 中使用的所有变量,但捕获 epsilon按值
  • [=, &epsilon] 按值捕获 lambda 中使用的所有变量,但按引用捕获 epsilon

生成的 operator()const 通过默认情况下,默认情况下访问它们时,捕获将是 const 。这会导致每次使用相同输入的调用都会产生相同的结果,但是您可以 将 lambda 标记为可变,以请求生成的operator() 不可变常量。

The problem

C++ includes useful generic functions like std::for_each and std::transform, which can be very handy. Unfortunately they can also be quite cumbersome to use, particularly if the functor you would like to apply is unique to the particular function.

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

If you only use f once and in that specific place it seems overkill to be writing a whole class just to do something trivial and one off.

In C++03 you might be tempted to write something like the following, to keep the functor local:

void func2(std::vector<int>& v) {
  struct {
    void operator()(int) {
       // do something
    }
  } f;
  std::for_each(v.begin(), v.end(), f);
}

however this is not allowed, f cannot be passed to a template function in C++03.

The new solution

C++11 introduces lambdas allow you to write an inline, anonymous functor to replace the struct f. For small simple examples this can be cleaner to read (it keeps everything in one place) and potentially simpler to maintain, for example in the simplest form:

void func3(std::vector<int>& v) {
  std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}

Lambda functions are just syntactic sugar for anonymous functors.

Return types

In simple cases the return type of the lambda is deduced for you, e.g.:

void func4(std::vector<double>& v) {
  std::transform(v.begin(), v.end(), v.begin(),
                 [](double d) { return d < 0.00001 ? 0 : d; }
                 );
}

however when you start to write more complex lambdas you will quickly encounter cases where the return type cannot be deduced by the compiler, e.g.:

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

To resolve this you are allowed to explicitly specify a return type for a lambda function, using -> 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;
            }
        });
}

"Capturing" variables

So far we've not used anything other than what was passed to the lambda within it, but we can also use other variables, within the lambda. If you want to access other variables you can use the capture clause (the [] of the expression), which has so far been unused in these examples, e.g.:

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

You can capture by both reference and value, which you can specify using & and = respectively:

  • [&epsilon, zeta] captures epsilon by reference and zeta by value
  • [&] captures all variables used in the lambda by reference
  • [=] captures all variables used in the lambda by value
  • [&, epsilon] captures all variables used in the lambda by reference but captures epsilon by value
  • [=, &epsilon] captures all variables used in the lambda by value but captures epsilon by reference

The generated operator() is const by default, with the implication that captures will be const when you access them by default. This has the effect that each call with the same input would produce the same result, however you can mark the lambda as mutable to request that the operator() that is produced is not const.

风和你 2024-12-14 15:39:55

什么是 lambda 表达式?

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

在 C++ 中,最小 lambda 表达式如下所示:

[]{} // lambda with no parameters that does nothing 

[] 是捕获列表,{} 是函数体。

lambda-expression 的完整语法,包括属性、noexcept/throw-规范、requires-clauses等比较复杂。

捕获列表

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

  1. 值:[x]
  2. 引用 [&x]
  3. 当前在引用范围内的任何变量 [&]
  4. 与 3 相同,但通过值 [=]
  5. 捕获 this 并使成员函数在 lambda [this] 内可调用

您可以混合任何上面的逗号分隔列表 [x, &y]

Init-captures (C++14)

现在可以使用 = 初始化捕获列表的元素,称为 init-capture
这允许重命名变量并通过移动来捕获。取自标准的一个示例:

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

模板参数 (C++20)

自 C++20 以来,lambda 表达式可以具有 < a href="https://eel.is/c++draft/temp.pre#nt:template-parameter-list" rel="noreferrer">template-parameter-list:

[]<int N>() {};

这样的 通用 lambda 就像一个非模板 struct 带有调用运算符模板:

struct __lambda {
    template <int N> void operator()() const {}
};

参数列表

parameter-declaration-clause 与任何其他 C++ 函数中的相同。
当没有参数时可以完全省略,即[](){}等同于[]{}

泛型 Lambda 函数 (C++14)

带有 auto 参数的 为 通用 lambda
如果此处 auto 相当于 T
T 是周围范围内某处的类型模板参数):

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

这就像 C++20 缩写函数模板

struct __lambda {
    // C++20 equivalent
    void operator()(auto x, auto y) const { return x + y; }
    // pre-C++20 equivalent
    template <typename T, typename U>
    void operator()(T x, U y) const { return x + y; }
};

返回类型(可能推导)

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

还可以使用尾随返回类型语法显式提供返回类型:

[](int x) -> int { return x; }

改进的返回类型推导 (C++14)

C++14 允许为每个函数推导返回类型,并且不将其限制为 return expression 形式的函数;。这也扩展到了 lambda。
默认情况下,lambda 的返回类型是按照其返回类型被声明为 auto 来推断的。

可变 lambda (C++14)

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

mutable 表示 lambda 类型的调用运算符没有 const 限定符。

函数体

A block-statement 将在以下情况下执行: lambda 实际上被称为。
这成为呼叫操作员的主体。

用例

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

What is a lambda expression?

The C++ concept of a lambda expression originates in the lambda calculus and functional programming.
A lambda is an unnamed function that is useful (in actual programming, not theory) for short snippets of code that are impossible to reuse and are not worth naming.

In C++, the minimal lambda expression looks like:

[]{} // lambda with no parameters that does nothing 

[] is the capture list and {} the function body.

The full syntax for a lambda-expression, including attributes, noexcept/throw-specifications, requires-clauses, etc. is more complex.

The capture list

The capture list defines what from the outside of the lambda should be available inside the function body and how.
It can be either:

  1. a value: [x]
  2. a reference [&x]
  3. any variable currently in scope by reference [&]
  4. same as 3, but by value [=]
  5. capturing this and making member functions callable within the lambda [this]

You can mix any of the above in a comma separated list [x, &y].

Init-captures (C++14)

An element of the capture list can now be initialized with =, which is called init-capture.
This allows renaming of variables and to capture by moving. An example taken from the standard:

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.

and one taken from Wikipedia showing how to capture with std::move:

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

The template parameters (C++20)

Since C++20, lambda expressions can have a template-parameter-list:

[]<int N>() {};

Such a generic lambda is like a non-template struct with a call operator template:

struct __lambda {
    template <int N> void operator()() const {}
};

The parameter list

The parameter-declaration-clause is the same as in any other C++ function.
It can be omitted completely when there are no parameters, meaning that [](){} is equivalent to []{}.

Generic Lambdas (C++14)

Lambdas with an auto parameter are generic lambdas.
auto would be equivalent to T here if
T were a type template argument somewhere in the surrounding scope):

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

This works just like a C++20 abbreviated function template:

struct __lambda {
    // C++20 equivalent
    void operator()(auto x, auto y) const { return x + y; }
    // pre-C++20 equivalent
    template <typename T, typename U>
    void operator()(T x, U y) const { return x + y; }
};

Return type (possibly deduced)

If a lambda has only one return statement, the return type can be omitted and has the implicit type of decltype(return_statement).

The return type can also be provided explicitly using trailing return type syntax:

[](int x) -> int { return x; }

Improved Return Type Deduction (C++14)

C++14 allows deduced return types for every function and does not restrict it to functions of the form return expression;. This is also extended to lambdas.
By default, the return type of a lambda is deduced as if its return type was declared auto.

Mutable lambda (C++14)

If a lambda is marked mutable (e.g. []() mutable { }) it is allowed to mutate the values that have been captured by value.

mutable means that the call operator of the lambda's type does not have a const qualifier.

The function body

A block-statement will be executed when the lambda is actually called.
This becomes the body of the call operator.

Use cases

The library defined by the ISO standard benefits heavily from lambdas and raises the usability several bars as now users don't have to clutter their code with small functors in some accessible scope.

独夜无伴 2024-12-14 15:39:55

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

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

Lambda expressions are typically used to encapsulate algorithms so that they can be passed to another function. However, it is possible to execute a lambda immediately upon definition:

[&](){ ...your code... }(); // immediately executed lambda expression

is functionally equivalent to

{ ...your code... } // simple code block

This makes lambda expressions a powerful tool for refactoring complex functions. You start by wrapping a code section in a lambda function as shown above. The process of explicit parameterization can then be performed gradually with intermediate testing after each step. Once you have the code-block fully parameterized (as demonstrated by the removal of the &), you can move the code to an external location and make it a normal function.

Similarly, you can use lambda expressions to initialize variables based on the result of an algorithm...

int a = []( int b ){ int r=1; while (b>0) r*=b--; return r; }(5); // 5!

As a way of partitioning your program logic, you might even find it useful to pass a lambda expression as an argument to another lambda expression...

[&]( std::function<void()> algorithm ) // wrapper section
   {
   ...your wrapper code...
   algorithm();
   ...your wrapper code...
   }
([&]() // algorithm section
   {
   ...your algorithm code...
   });

Lambda expressions also let you create named nested functions, which can be a convenient way of avoiding duplicate logic. Using named lambdas also tends to be a little easier on the eyes (compared to anonymous inline lambdas) when passing a non-trivial function as a parameter to another function. Note: don't forget the semicolon after the closing curly brace.

auto algorithm = [&]( double x, double m, double b ) -> double
   {
   return m*x+b;
   };

int a=algorithm(1,2,3), b=algorithm(4,5,6);

If subsequent profiling reveals significant initialization overhead for the function object, you might choose to rewrite this as a normal function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文