Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How much does a C++11 lambda capture actually capture?

I've seen in multiple examples that you can use a single character to capture multiple variables like the following:

Rect rect;
Point point;

auto someLambda = [&](const SomeType& var)
{
    if (rect.Contains(point))
    {
        var.Something();
    }

    this->MemberFunction();
};

This ends up grabbing rect and point by reference and also gives you access to this, but how much does it actually capture? Does it only capture the variables that it needs, or does it capture literally everything there is in the current scope?

I've seen in other examples that you can also specify individual variables to capture like this:

Rect rect;
Point point;

auto someLambda = [this, &rect, &point](const SomeType& var)
{
    if (rect.Contains(point))
    {
        var.Something();
    }

    this->MemberFunction();
};

Is there any advantage to doing it one way or the other? Someone I worked with once mentioned that using the "capture all" [&] version was more expensive but I can't find any documentation to back that up. I just want to know for sure so I'm not making code more complex than it needs to be or doing expensive things that I shouldn't be doing.

like image 393
Justin G Avatar asked May 09 '16 22:05

Justin G


People also ask

What is lambda function in C++11?

In C++11 and later, a lambda expression—often called a lambda—is a convenient way of defining an anonymous function object (a closure) right at the location where it's invoked or passed as an argument to a function.

How does lambda capture work?

By default, variables are captured by const value . This means when the lambda is created, the lambda captures a constant copy of the outer scope variable, which means that the lambda is not allowed to modify them. In the following example, we capture the variable ammo and try to decrement it.

What does it mean to lambda capture this?

In Short. A lambda defined inside a non-static member function can directly access the members of the current object (or its copy) via an appropriate capture clause. But how the current object can be captured has gone through some changes since C++11.

What is capture clause in lambda function in C++?

A capture clause of lambda definition is used to specify which variables are captured and whether they are captured by reference or by value. An empty capture closure [ ], indicates that no variables are used by lambda which means it can only access variables that are local to it.


2 Answers

According to http://en.cppreference.com/w/cpp/language/lambda, the capture list (the part in the square braces) is:

a comma-separated list of zero or more captures, optionally beginning with a capture-default. Capture list can be passed as follows [...]:

[a,&b] where a is captured by value and b is captured by reference.

[this] captures the this pointer by value

[&] captures all automatic variables odr-used in the body of the lambda by reference

[=] captures all automatic variables odr-used in the body of the lambda by value

[] captures nothing

This means that only the automatic (scope-lifetime) variables used in the body of the lambda will be captured.

I can't see why capturing everything with [&] would be more expensive than individual captures, but one advantage of listing out the captures explicitly is that there's no chance of capturing something you didn't expect.

On the other hand, capturing with [=] could prove expensive since it will make copies of everything. Perhaps that's what your coworker was referring to.

like image 147
jb326 Avatar answered Oct 14 '22 05:10

jb326


This ends up grabbing rect and point by reference and also gives you access to this, but how much does it actually capture?

When you capture with [&] it captures all the variables in the body of the lambda that have automatic storage duration and are odr-used.

Here is an example:

int main()
{
    int num = 50;
    [&] { std::cout << num << '\n'; }(); // num captured by reference
    [=] { std::cout << num << '\n'; }(); // num captured by value

    [&num] { std::cout << num << '\n'; }(); // by reference
    [num] { std::cout << num << '\n'; }();  // by value
}

If you know that you will only capture one variable, you don't need to capture everything by value / reference.

But on the other hand, if you know that you will be capturing a few variables, capturing them with [&] or [=] is easier than typing them all out.

like image 26
Andreas DM Avatar answered Oct 14 '22 07:10

Andreas DM