224

The following code compiles with gcc 4.5.1 but not with VS2010 SP1:

#include <iostream>
#include <vector>
#include <map>
#include <utility>
#include <set>
#include <algorithm>
 
using namespace std;
class puzzle
{
    vector<vector<int>> grid;
    map<int,set<int>> groups;
public:
    int member_function();
};
 
int puzzle::member_function()
{
    int i;
    for_each(groups.cbegin(), groups.cend(), [grid, &i](pair<int,set<int>> group) {
        i++;
        cout << i << endl;
    });
}

This is the error:

error C3480: 'puzzle::grid': a lambda capture variable must be from an enclosing function scope
warning C4573: the usage of 'puzzle::grid' requires the compiler to capture 'this' but the current default capture mode does not allow it
  1. Which compiler is right?
  2. How can I use data members inside a lambda in VS2010?
3
  • 1
    Note: It should be pair<const int, set<int> >, that's the actual pair-type of a map. It should possibly also be a reference-to-const. Commented Oct 25, 2011 at 21:21
  • Related; very helpful: thispointer.com/… Commented Apr 28, 2020 at 5:10
  • use [&] to capture by reference. Commented Nov 26, 2021 at 23:16

4 Answers 4

234

Summary of the alternatives:

capture this:

auto lambda = [this](){};

use a local reference to the member:

auto& tmp = grid;
auto lambda = [ tmp](){}; // capture grid by (a single) copy
auto lambda = [&tmp](){}; // capture grid by ref

C++14:

auto lambda = [ grid = grid](){}; // capture grid by copy
auto lambda = [&grid = grid](){}; // capture grid by ref

example: https://godbolt.org/g/dEKVGD

Sign up to request clarification or add additional context in comments.

5 Comments

Interesting that only explicit use of the capture with initializer syntax works for this (i.e. in C++14 just doing [&grid] still doesn't work). Very glad to know this!
Good summary. I find the C++14 syntax very convenient
Note that the variable name can be anything: auto lambda = [&anyName = grid](){};
Thanks for the useful answer. I think it could be improved with a brief explanation on the differences between the options. Has the C++14 notation performance differences or any other advantage from the "capture this" option?
If you use references it generates the same code: gcc.godbolt.org/z/58TTjPqob It's more about limiting the amount of state you can access I guess, or to make it more explicit. Of course when copying objects it's much better to only copy parts of this instead of the whole *this.
201

I believe VS2010 to be right this time, and I'd check if I had the standard handy, but currently I don't.

Now, it's exactly like the error message says: You can't capture stuff outside of the enclosing scope of the lambda. grid is not in the enclosing scope, but this is (every access to grid actually happens as this->grid in member functions). For your usecase, capturing this works, since you'll use it right away and you don't want to copy the grid

auto lambda = [this](){ std::cout << grid[0][0] << "\n"; }

If however, you want to store the grid and copy it for later access, where your puzzle object might already be destroyed, you'll need to make an intermediate, local copy:

vector<vector<int> > tmp(grid);
auto lambda = [tmp](){}; // capture the local copy per copy

† I'm simplifying - Google for "reaching scope" or see §5.1.2 for all the gory details.

5 Comments

It seems quite limited to me. I don't understand why a compiler would need to prevent such a thing. It works well with bind, although the syntax is horrible with the ostream left shift operator.
Could tmp be a const & to grid to cut down on copying? We still want at least one copy, the copy into the lambda ([tmp]), but no need for a second copy.
The solution might make an unnecessary extra copy of grid though it probably gets optimized out. Shorter and better is: auto& tmp = grid; etc.
If you have C++14 available, you could do [grid = grid](){ std::cout << grid[0][0] << "\n"; } to avoid the extra copy
It seems to be fixed in gcc 4.9 ( and gcc 5.4 for that matter) error: capture of non-variable ‘puzzle::grid’
23

I believe, you need to capture this.

8 Comments

This is correct, it will capture the this-pointer and you can still just refer to grid directly. Problem being, what if you want to copy the grid? This won't allow you to do that.
You can, but only in a roundabout way: You have to make a local copy, and capture that in the lambda. That's just the rule with lambdas, you can't capture stiff outside of the enclosing scope.
Sure you can copy. I meant you can't copy-capture it, of course.
What I described does a copy capture, through the intermediate local copy - see my answer. Aside from that, I don't know any way to copy capture a member variable.
Sure, it does copy capture, but not the member. It involves two copies unless compiler is smarter than usual, I'd guess.
|
16

An alternate method that limits the scope of the lambda rather than giving it access to the whole this is to pass in a local reference to the member variable, e.g.

auto& localGrid = grid;
int i;
for_each(groups.cbegin(),groups.cend(),[localGrid,&i](pair<int,set<int>> group){
            i++;
            cout<<i<<endl;
   });

1 Comment

I love your idea: using a fake reference variable and pass it to capture list :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.