[try Beta version]
Not logged in

 
Lambda capture

Aug 3, 2017 at 7:52pm
If there is a lambda capture [=, &var] does it mean that it can access all the variables in the scope in which it is defined by value except for var? Also does [&var, =] mean the same thing?
Aug 4, 2017 at 12:00am
If there is a lambda capture [=, &var] does it mean that it can access all the variables in the scope in which it is defined by value except for var?

Yes, except that if there is a current object, it (i.e. *this) is also captured by reference.

This is because if the current object is implicitly bound it always captured by reference. To capture the current object by value it must be explicitly captured by value: ([=, *this])

Also does [&var, =] mean the same thing?
No, because that's a syntax error. The capture-default must appear first in the capture list.
Aug 4, 2017 at 7:49am
mbozzi wrote:
Yes, except that if there is a current object, it (i.e. *this) is also captured by reference.

This is because if the current object is implicitly bound it always captured by reference.

@mbozzi, could you please explain me better this point?
Aren’t all pointers captured as they are, i.e. pointers?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <limits>

void waitForEnter();

int main()
{
    int* pint = new int(5);
    std::cout << "pint: " << *pint << '\n';
    [=]{ (*pint)++; } ();
    std::cout << "pint: " << *pint << '\n';
    waitForEnter();
    return 0;
}

void waitForEnter()
{
    std::cout << "\nPress ENTER to continue...\n";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

output:
pint: 5
pint: 6

Press ENTER to continue...

What am I missing?
Aug 4, 2017 at 7:50pm
Aren’t all pointers captured as they are, i.e. pointers?

C++ has idiosyncratic rules about this in a lambda capture list. If the form this appears in a capture list, or it is captured implicitly, the members bound by the current object *this are visible in the closure, roughly like the lambda expression was a member function. Plain old this can only be captured by value (because the capture &this is a syntax error), and it allows transparent access to the members of the current object:

1
2
3
4
struct A { 
  int x = 0;
  int f() { [this]() { return x++; }(); } // A::x is visible.  
} a;

Each time you call a.f(), a.x is incremented again.

C++17 supplies a mechanism to copy the current object into the lambda by explicitly adding *this to the capture list:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# include <iostream>

struct A {
  // Note: *this is captured by reference.
  auto make_get_n_ref() { return [this](){ return n; }; }
  // Note: *this is captured by value
  auto make_get_n_val() { return [*this](){ return n; }; }
  int n = 1; 
};

int main() {
  A a;
  // the value that foo() will return is fixed at the 
  // point the closure was created
  auto foo = a.make_get_n_val();  
  
  // the value that bar() will return depends on the value of a.n 
  // at the time of the call
  auto bar = a.make_get_n_ref();
 
  std::cout << foo() << ", " << bar() << '\n';
  a.n = 2;
  std::cout << foo() << ", " << bar() << '\n';
}

http://coliru.stacked-crooked.com/a/cbfd422b569b5330
Last edited on Aug 4, 2017 at 9:03pm
Aug 4, 2017 at 9:38pm
Uh, I see what you mean. Thanks a lot for your kind explanation, mbozzi, I didn’t know it was possible to capture the current object by copy (it sounds potentially expensive, anyway).
Topic archived. No new replies allowed.