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

10

1760

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.

Parolee answered 2/10, 2011 at 14:58 Comment(2)
I've seen a case where the lambda was very useful: A colleague of me was doing code that has millions of iterations to solve a space optimization problem. The algorithm was much more speedy when using a lambda than a proper function! The compiler is Visual C++ 2013.Stapleton
Here is another really good reference which explains very well what are lambda expressions in C++: Microsoft.com: Lambda expressions in C++. I especially like how well it explains the parts of a lambda expression, in particular: the capture clause, parameter list, trailing-return-type, and lambda body.Dupe
E
1783

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.

Entire answered 2/10, 2011 at 15:21 Comment(18)
Does mutable make operator() non-const, or does it make the copies of the local variables mutable? Conclusion it simply makes the state of the lambda mutable, operator() remains const.Colleen
@Yakk you have been trapped. lambdas without a capture have an implicit conversion to function type pointers. the conversion function is const always...Geostatic
@JohannesSchaub-litb oh sneaky -- and it happens when you invoke () -- it is passed as a zero-argument lambda, but because () const doesn't match the lambda, it looks for a type conversion which allows it, which includes implicit-cast-to-function-pointer, and then calls that! Sneaky!Colleen
Interesting - I originally thought that lambdas were anonymous functions rather than functors, and was confused about how captures worked.Cookstove
If you want to use lambdas as variables in your program, you can use: std::function<double(int, bool)> f = [](int a, bool b) -> double { ... }; But usually, we let the compiler deduce the type: auto f = [](int a, bool b) -> double { ... }; (and don't forget to #include <functional>)Christian
It seems like there should be a note about how reference-based capture should be used with caution when storing the lambda in a variable that can potentially exist beyond the scope of what's being referenced.Cosy
That's always true of references.Entire
I suppose not everyone understands why return d < 0.00001 ? 0 : d; is guaranteed to return double, when one of the operands is an integer constant (it is because of an implicit promotion rule of the ?: operator where the 2nd and 3rd operand are balanced against each other through the usual arithmetic conversions no matter which one that gets picked). Changing to 0.0 : d would perhaps make the example easier to understand.Developing
quite old post. However, in your first example I dont understand "f cannot be passed to a template function in C++03". What is the difference to func where f is passed to a template function. Well, while writing this I realize that the difference is that in func f is has a unnamed type. Nevertheless I think the (already very helpful) answer could be improved by adding this detail.Vernita
Difference is that f lives inside a function - the rules forbid doing that.Entire
A thing to note here regarding motivation of usage in lambdas, is in asynchronous programming, as far as I understand.Sauncho
Just a nit: the first example doesn't need the struct f; you can simply define a function directly. That does away with part of the overkill silliness.Mascagni
How to pass an array in the capture clause?Mcbrayer
@MohammadMamunHossain use std::array array instead of raw arrays and then it becomes trivial. (Which is good advice in most cases in C++ anyway)Entire
hey, by saying all variables used in the lambda, what does it mean, what is the scope?Seclusive
any variable referenced within the {} of the lambdaEntire
Why capture a variable and not pass it as parameter to the lambda?Laster
@Laster usually you want the lambda to have a certain signature so you can had it to someone else. Typically that signature doesn't allow for all the details of what the lambda does to be passed as a parameter.Entire
I
947

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.

Interglacial answered 2/10, 2011 at 15:43 Comment(5)
In your example for initialized lambda captures above, why do you end the lamba function with the ();? This appears like [](){}(); instead of [](){};. Also shouldn't the value of x be 5?Frowsy
@RamakrishnanKannan: 1) the () are there to call the lambda right after defining it and give y its return value. The variable y is an integer, not the lambda. 2) No, x=5 is local to the lambda (a capture by value which just happens to have the same name as the outer scope variable x), and then x+2 = 5+2 is returned. The reassignment of the outer variable x happens through the reference r: r = &x; r += 2;, but this happens to the original value of 4.Shortridge
hey, by saying any variable currently in scope, what does it mean? it means capture all global variables globally and any local variables in this function?Seclusive
I saw in the documentation that a Throw has been added: learn.microsoft.com/en-us/cpp/cpp/…Fieldstone
@TheVee commenting 7 years later just to say that example with different x inside and outside the lambda is mean. Took me a second to get why that was the result :)Thormora
S
182

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.

Skiba answered 1/3, 2013 at 8:8 Comment(15)
Have you realized that this question was asked 1.5 years ago and that the last activity was almost 1 year ago? Anyway, you're contributing some interesting ideas I haven't seen before!Memory
Thanks for the simultaneous define-and-execute tip! I think it's worth noting that that works in as a contidion for if statements: if ([i]{ for (char j : i) if (!isspace(j)) return false ; return true ; }()) // i is all whitespace, assuming i is an std::stringTheron
So the following is a legal expression: [](){}();.Skiba
I don't understand the last point named nested functions. It looks like double algorithm(double x,double m, double b) {return m*x+b;} does the same thing as the lambda example and is shorter and more compatible.Ridgley
@MarkLakata: A nested function can be declared inside another function, and can even reference the outer function's data if desired.Skiba
@nobar - you're right, I mistyped. This is legal (I tested it this time) main() {{{{((([](){{}}())));}}}}Ridgley
@nobar - I suggest editing your algorithm example so that it uses a non empty capture [] to demonstrate why you would actually use a named nested function rather than a plain old static function. Otherwise (to the C++11 beginner such as myself) they look like they provide the same functionality.Ridgley
@MarkLakata: You can follow the provided link (*) for a deeper explanation (it goes beyond what you have identified). My example demonstrates the basic syntax, but doesn't cover all of the possible use-cases.Skiba
I think lambda expression can not have a name until you say that we can assign it to a object, yeah!Chrystel
@Chrystel That object would be a std::function if I am understanding you correctly. You can store any callable object in it and call it later. Functions like for_each will probably have a std::function parameter.Tomtoma
Aside: JavaScript uses this type of construct for purposes of creating a lexical scope. IIFESkiba
Related question: How to immediately invoke a C++ lambda?Skiba
JavaScript calls this pattern 'IIFE' (Immediately Invoked Function Expression). I just encountered a couple of sources calling it 'IILE' (Immediately Invoked Lambda Expresssion) when used in C++.Skiba
(lambda: None)() or [](){}(); for some reason I like the second. To me, it's more mathematical, less verbose. All this need for comprehension :) tskDestroy
A lot of the formal documentation for C++ Lambda refers to the obscure term 'closures'. 'Function closures' defined simply (for Go-lang): "A closure is a function value that references variables from outside its body." -- from A Tour of Go, Function closures. C++ has the unique feature of allowing you to be explicit about the closure arguments -- which enables the refactoring use-case that I mention in the answer.Skiba
C
47

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);};
    
Caducous answered 3/6, 2015 at 16:40 Comment(4)
This [&,=Param2](int arg1){} doesn't seem to be valid syntax. The correct form would be [&,Param2](int arg1){}Effect
Thanks. First I tried to compile this snippet. And it seems strange assymetry in allowable modificators in capture list // 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", param); } #endif #if 0 { int param = 0; auto f=[&,=param](int arg1) mutable {param = arg1;}; f(111); printf("%i\n", param); } #endif return 0; }Caducous
Looks that new line in not supported in comment. Then I opened 5.1.2 Lambda expressions, p.88, "Working Draft, Standard for Programming Language C ++", Dcoument Number: #337, 2012-01-16. And looked into grammar syntax. And you're right. There is no exist such thing like capture via "=arg"Caducous
Big Thanks, fixed it in description and also acquire new knowledge w.r.t. to it.Caducous
D
18

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');
    });
}
Deleterious answered 10/3, 2015 at 22:36 Comment(5)
the reason is, that a lambda can only convert to a function pointer, if it has no capture. if apply was a template that accepted a functor, it would workHerzberg
But the problem is that if apply is an existing interface, you may not have the luxury of being able to declare it differently than a plain old function. The standard could have been designed to allow a new instance of a plain old function to be generated each time such a lambda expression is executed, with generated hard-coded references to the captured variables. It seems a lambda function is generated at compile time. There are other consequences as well. e.g., If you declare a static variable, even if you re-evaluate the lambda expression, you don't get a new static variable.Deleterious
function pointer are often meant to be saved, and a lambdas capture can go out of scope. that only capture-less lambdas convert to function-pointers was by designHerzberg
You still have to pay attention to stack variables being deallocated for the same reason either way. See blogs.msdn.com/b/nativeconcurrency/archive/2012/01/29/… The example I wrote with output and apply is written so that if instead function pointers were allowed and used, they would work as well. The col remains allocated until after all of the function calls from apply have finished. How would you rewrite this code to work using the existing apply interface? Would you end up using global or static variables, or some more obscure transformation of the code?Deleterious
or perhaps you simply mean that lambda expressions are rvalues and therefore temporary, yet the code remains constant (singleton/static) so that it can be called in the future. In that case, perhaps the function should remain allocated as long as its stack-allocated captures remain allocated. Of course it could get messy unwinding it if for example many variations of the function are allocated in a loop.Deleterious
P
17

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:

Portal answered 9/11, 2016 at 11:2 Comment(2)
Nice explanation. Using range-based for loops, you can avoid lambdas and shorten the code for (int x : v) { if (x % m == 0) os << x << '\n';}Demoiselle
finally a catchable answer, like "What is it?", "What are the use cases?" .Collision
B
6

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.

Baruch answered 4/6, 2019 at 10:22 Comment(2)
yes its literally on the go, you define it; use it; and as the parent function scope finishes the lambda function is gone .. what if the function returns the lambda to the caller?Parolee
It's also not the fastest one compare to normal function. because it has some overhead which need to be handled by lambda. Have you ever actually run any benchmark to support this claim? On the contrary, lambda + templates often produce fastest code possible.Parolee
I
3

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.

Interplead answered 23/11, 2015 at 9:16 Comment(1)
Not sure that this is the best example of when to use lambdas. Here's a simpler and shorter way to write the same thing in old fashioned C++.Bette
F
2

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
Freddyfredek answered 29/6, 2021 at 14:23 Comment(0)
S
0

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.

Stapleton answered 27/6, 2015 at 0:38 Comment(1)
This can also be done with a plain function, which is even what the accepted answer to the question you linked to says to do.Essy

© 2022 - 2024 — McMap. All rights reserved.