Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In lambda functions syntax, what purpose does a 'capture list' serve?

Tags:

Taken from an answer to this question, as an example, this is a code that calculates the sum of elements in a std::vector:

std::for_each(     vector.begin(),     vector.end(),     [&](int n) {         sum_of_elems += n;     } ); 

I understand that lambda functions are just nameless functions.

I understand the lambda functions syntax as explained here.

I do not understand why lambda functions need the capture list, while normal functions do not.

  1. What extra information does a capture list provide?
  2. Why do normal functions not need that information?
  3. Are lambda functions more than just nameless functions?
like image 840
Lazer Avatar asked Mar 04 '12 10:03

Lazer


People also ask

What does capture mean in lambda?

The lambda is capturing an outside variable. A lambda is a syntax for creating a class. Capturing a variable means that variable is passed to the constructor for that class. A lambda can specify whether it's passed by reference or by value.

What is the purpose of lambda functions?

What exactly are lambda functions? Lambda functions are intended as a shorthand for defining functions that can come in handy to write concise code without wasting multiple lines defining a function. They are also known as anonymous functions, since they do not have a name unless assigned one.

What is the syntax of defining lambda expression?

A lambda expression is an anonymous function that provides a concise and functional syntax, which is used to write anonymous methods. It is based on the function programming concept and used to create delegates or expression tree types. The syntax is function(arg1, arg2... argn) expression.

What is the purpose of using lambda function in Python?

We use lambda functions when we require a nameless function for a short period of time. In Python, we generally use it as an argument to a higher-order function (a function that takes in other functions as arguments). Lambda functions are used along with built-in functions like filter() , map() etc.


2 Answers

From the syntax link you gave, the capture list "defines what from the outside of the lambda should be available inside the function body and how"

Ordinary functions can use external data in a few ways:

  1. Static fields
  2. Instance fields
  3. Parameters (including reference parameters)
  4. Globals

Lambda add the ability to have one unnamed function within another. The lambda can then use the values you specify. Unlike ordinary functions, this can include local variables from an outer function.

As that answer says, you can also specify how you want to capture. awoodland gives a few exampls in another answer. For instance, you can capture one outer variable by reference (like a reference parameter), and all others by value:

[=, &epsilon] 

EDIT:

It's important to distinguish between the signature and what the lambda uses internally. The signature of a lambda is the ordered list of parameter types, plus the type of the returned value.

For instance, a unary function takes a single value of a particular type, and returns a value of another type.

However, internally it can use other values. As a trivial example:

[x, y](int z) -> int  {    return x + y - z; } 

The caller of the lambda only knows that it takes an int and returns an int. However, internally it happens to use two other variables by value.

like image 140
Matthew Flaschen Avatar answered Oct 27 '22 23:10

Matthew Flaschen


The basic problem that we're trying to solve is that some algorithm expects a function that only takes a particular set of arguments (one int in your example). However, we want the function to be able to manipulate or inspect some other object, maybe like so:

void what_we_want(int n, std::set<int> const & conditions, int & total) {     if (conditions.find(n) != conditions.end()) { total += n; } } 

However, all we are allowed to give our algorithm is a function like void f(int). So where do we put the other data?

You could either keep the other data in a global variable, or you could follow the traditional C++ approach and write a functor:

struct what_we_must_write {     what_we_must_write(std::set<int> const & s, int & t)     : conditions(s), total(t)     {   }      void operator()(int n)     {         if (conditions.find(n) != conditions.end()) { total += n; }     } private:    std::set<int> const & conditions;    int & total; }; 

Now we can call the algorithm with a suitably initialized functor:

std::set<int> conditions; int total;  for_each(v.begin(), v.end(), what_we_must_write(conditions, total)); 

Finally, a closure object (which is described by a lambda expression) is just that: A short-hand way of writing a functor. The equivalent of the above functor is the lambda

auto what_we_get = [&conditions, &total](int n) -> void {     if (condiditons.find(n) != conditions.end()) { total += n; } }; 

The short-hand capture lists [=] and [&] just capture "everything" (respectively by value or by reference), which means that the compiler figures out the concrete capture list for you (it doesn't actually put everything into the closure object, but only the things you need).

So, in nutshell: A closure object without capture is like a free function, and a closure with capture is like a functor object with suitably defined and initialized private member objects.

like image 41
Kerrek SB Avatar answered Oct 27 '22 23:10

Kerrek SB