Using member variable in lambda capture list inside a member function

191,511

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;
   });
Share:
191,511

Related videos on Youtube

vivek
Author by

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, 2022

Comments

  • vivek
    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
      Xeo over 12 years
      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.
    • Gabriel Staples
      Gabriel Staples about 4 years
      Related; very helpful: thispointer.com/…
    • mr_azad
      mr_azad over 2 years
      use [&] to capture by reference.
  • Xeo
    Xeo over 12 years
    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.
  • Xeo
    Xeo over 12 years
    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.
  • Michael Krelin - hacker
    Michael Krelin - hacker over 12 years
    Sure you can copy. I meant you can't copy-capture it, of course.
  • Xeo
    Xeo over 12 years
    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.
  • Michael Krelin - hacker
    Michael Krelin - hacker over 12 years
    Sure, it does copy capture, but not the member. It involves two copies unless compiler is smarter than usual, I'd guess.
  • Xeo
    Xeo over 12 years
    We 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
    Michael Krelin - hacker over 12 years
    I think we both mean the same thing for quite a while now :)
  • Jean-Simon Brochu
    Jean-Simon Brochu over 10 years
    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.
  • Aaron McDaid
    Aaron McDaid almost 9 years
    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.
  • Tom Swirly
    Tom Swirly almost 9 years
    The solution might make an unnecessary extra copy of grid though it probably gets optimized out. Shorter and better is: auto& tmp = grid; etc.
  • Emadpres
    Emadpres over 8 years
    I love your idea: using a fake reference variable and pass it to capture list :)
  • sigy
    sigy over 8 years
    If you have C++14 available, you could do [grid = grid](){ std::cout << grid[0][0] << "\n"; } to avoid the extra copy
  • mattsilver
    mattsilver over 7 years
    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!
  • Andrew
    Andrew about 7 years
    For "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
    tuket almost 6 years
    Good summary. I find the C++14 syntax very convenient
  • BGabor
    BGabor over 4 years
    It seems to be fixed in gcc 4.9 ( and gcc 5.4 for that matter) error: capture of non-variable ‘puzzle::grid’