Using member variable in lambda capture list inside a member function
Solution 1
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.
Solution 2
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
Solution 3
I believe, you need to capture this
.
Solution 4
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;
});
Related videos on Youtube
vivek
A Post-Grad in chemistry. Programming is my hobby. I like c++ and python, into scala ad Clojure these days, and completely hate Java.
Updated on July 29, 2022Comments
-
vivek almost 2 years
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; }); } int main() { return 0; }
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
So,
1> which compiler is right?
2> How can I use member variables inside a lambda in VS2010?
-
Xeo over 12 yearsNote: 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. -
Gabriel Staples about 4 yearsRelated; very helpful: thispointer.com/…
-
mr_azad over 2 yearsuse [&] to capture by reference.
-
-
Xeo over 12 yearsThis 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. -
Xeo over 12 yearsYou 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.
-
Michael Krelin - hacker over 12 yearsSure you can copy. I meant you can't copy-capture it, of course.
-
Xeo over 12 yearsWhat 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.
-
Michael Krelin - hacker over 12 yearsSure, it does copy capture, but not the member. It involves two copies unless compiler is smarter than usual, I'd guess.
-
Xeo over 12 yearsWe can get pedantic that this can be seen as a copy capture of the member, but lets just say that we both mean the same thing, ok? :)
-
Michael Krelin - hacker over 12 yearsI think we both mean the same thing for quite a while now :)
-
Jean-Simon Brochu over 10 yearsIt 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.
-
Aaron McDaid almost 9 yearsCould
tmp
be aconst &
togrid
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. -
Tom Swirly almost 9 yearsThe solution might make an unnecessary extra copy of
grid
though it probably gets optimized out. Shorter and better is:auto& tmp = grid;
etc. -
Emadpres over 8 yearsI love your idea: using a fake reference variable and pass it to capture list :)
-
sigy over 8 yearsIf you have C++14 available, you could do
[grid = grid](){ std::cout << grid[0][0] << "\n"; }
to avoid the extra copy -
mattsilver over 7 yearsInteresting 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! -
Andrew about 7 yearsFor "capturing that", see my answer here, which is in an entirely different answer and context but applies just as well here (just ignore the async part): stackoverflow.com/a/42193934/1599699
-
tuket almost 6 yearsGood summary. I find the C++14 syntax very convenient
-
BGabor over 4 yearsIt seems to be fixed in gcc 4.9 ( and gcc 5.4 for that matter)
error: capture of non-variable ‘puzzle::grid’