Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to prefer explicit capture in lambda over implicit captures?

Tags:

c++

c++11

lambda

There are sometimes cases where I do a std::find_if (for example) in a local function that has 5 local variables, including parameters. However, the lambda I pass into the STL algorithm only needs access to 1 of those. I could capture this in one of two ways:

void foo(int one, int two, int three)
{
    std::vector<int> m_numbers;
    int four, five;

    std::find_if(m_numbers.begin(), m_numbers.end(), [=](int number) {
        return number == four;
    });
}

Or I can do:

void foo(int one, int two, int three)
{
    std::vector<int> m_numbers;
    int four, five;

    std::find_if(m_numbers.begin(), m_numbers.end(), [four](int number) {
        return number == four;
    });
}

(Note I didn't compile this code, apologies for any syntax errors or other mistakes)

I know that implicit captures are based on odr-used rules, so functionally and implementation-wise, I think both are identical. When would you use explicit captures over implicit ones? My only thought is somewhat related to principles of encapsulation: Having access to only the stuff you need allows the compiler to help you determine when you access a variable you shouldn't. It also keeps the local state of the method (it's invariants, for the lifetime of the function during its execution) safer. But are these really practical concerns?

Are there functional reasons to use explicit captures over implicit ones? What is a good rule of thumb or best practice to follow?

like image 461
void.pointer Avatar asked Dec 22 '15 16:12

void.pointer


People also ask

What is implicit capture in lambda expression?

Any entity captured by a lambda (implicitly or explicitly) is odr-used by the lambda-expression (therefore, implicit capture by a nested lambda triggers implicit capture in the enclosing lambda). All implicitly-captured variables must be declared within the reaching scope of the lambda.

What is the default capture in lambda?

Lambda capture. The captures is a comma-separated list of zero or more captures, optionally beginning with the capture-default. The only capture defaults are. & (implicitly capture the used automatic variables by reference) and. = (implicitly capture the used automatic variables by copy).

Can a lambda expression use a variable without capturing it?

A lambda expression can use a variable without capturing it if the variable is a non-local variable or has static or thread local storage duration (in which case the variable cannot be captured), or is a reference that has been initialized with a constant expression.

What is the purpose of the capture list?

The capture list defines the outside variables that are accessible from within the lambda function body. The only capture defaults are = (implicitly capture the used automatic variables by copy).


2 Answers

  • Explicit capture is always preferable as it is less error-prone
  • It is better to use & in case of heavy objects (not simple int, double etc)
  • Use = when you plan to use your lambda outside the scope of the variable capturing. With & it is risky to get dangling reference to the local destroyed variable
like image 58
paceholder Avatar answered Oct 23 '22 18:10

paceholder


It is simplest and the most efficient at runtime to use [=] or [&], without naming any names.

In these cases, as described by this answer, variables are only captured if they are odr-used. In other words the compiler only captures what is needed.

If you specify a capture list then two differences can happen:

  • You forget to capture something that the lambda uses
  • You capture something that the lambda didn't need.

In the second case, if capturing by value, this means the object is unnecessarily copied.

So, my advice would be to use [] , [&], or [=] unless you can think of a good reason otherwise for a specific situation. One such case might be if you wanted to capture some variables by reference and some by value.

like image 4
M.M Avatar answered Oct 23 '22 17:10

M.M