12

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?

void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • 2
    IIRC, Scott Meyers suggests (in his newest book) to pretty much only use implicit captures in cases like this, where it's an argument to a function that uses it and doesn't keep it around. There are, of course, examples of traps you can fall into when using implicit captures in various scenarios. – chris Dec 22 '15 at 16:50
  • Examples of such traps would be greatly appreciated. Those examples will help contrast the differences and help establish good general practice rules. – void.pointer Dec 22 '15 at 17:16
  • 8
    @chris The recommendation of Scott Meyers goes other way around. Effective Modern C++, Item 31: Avoid default capture modes. – LogicStuff Dec 22 '15 at 17:28
  • 1
    @LogicStuff, That's what I was trying to say, with implicit meaning default, and using that sparingly, perhaps only in this specific scenario. – chris Dec 22 '15 at 17:33
  • I actually have Effective Modern C++, I will read Item 31, hopefully that is a good start to finding the answer to my question. – void.pointer Dec 22 '15 at 21:16

2 Answers2

8
  • 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
paceholder
  • 1,064
  • 1
  • 10
  • 21
5

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.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365