Understanding Lambda-Capture

A lambda expression can refer to identifiers declared outside the lambda expression. If the identifier is a local variable or a reference with automatic storage duration, it is an up-level reference and must be "captured" by the lambda expression. Such a lambda expression must be introduced by [lambda-captureopt], where lambda-captureopt specifies whether identifiers are captured by reference or by copy. The table below summarizes forms of lambda-captureopt.

Symbol

Indicates

[]

Nothing to capture: an up-level reference is an error

[&x, y, ...]

Capture as specified: identifiers prefixed by & are captured by reference; other identifiers are captured by copy. An up-level reference to any variable not explicitly listed is an error

[&]

Capture by reference: an up-level reference implicitly captures the variable by reference

[=]

Capture by copy: an up-level reference implicitly captures the variable by copy

[&, x, y, ...]

Capture by reference with exceptions: listed variables are captured by value/copy (no listed variable may be prefixed by &)

[=, &x, &y, ...]

Capture by copy with exceptions: listed variables are captured by reference only (every listed variable must be prefixed by &)

No identifier may appear twice in the list. In the following code that sets area to the sum of the areas of four circles, the notation [&area,pi] specifies that area is captured by reference and pi by copy.

float radius[] = {2,3,5,7};

float area=0;

float pi = 3.14f;

for_each(radius, radius+4, [&area,pi](float r) {return area+=pi*r*r;})

Specifying Default Capture

When a default capture is specified, the list must specify only the other kind of capture. In other words, if you specify that the default capture is by reference, then you must list (and only list) the variables that should be captured by copy. For example, if your intent is to capture x by reference and y by copy, and both x and y appear in the function body, the following code illustrates what is correct and incorrect code:

[&,&x,y]

 //ERROR - default is capture-by-reference; list only capture-by-copy variable, y

 

[&,y]

 //CORRECT - default is capture-by-reference; listed variable, y, should not have & prefix

 

[=,&x,y]  

 //ERROR - default is capture-by-copy; listed variable, x, must be prefixed with &

 

[=,&x]  

 //CORRECT - default is capture by copy; listed variable must be prefixed with &

 

[&x]   

 //ERROR - no default capture; you must list y separately to be captured by copy

 

[y]    

 //ERROR - again no default capture is specified, so you must list &x separately to be captured by reference

 

[&x,y]   

 //CORRECT - since no default is specified, every variable is listed with its capture mode.

Default Binding Modes

The following lambda expressions demonstrate default binding modes. All three expressions are semantically equivalent. Each captures x and y by reference, and captures a and b by copy.

[&x,&y,a,b](float r) {x=a; y=b;}

 

[&,a,b](float r) {x=a; y=b;}

 

[=,&x,&y](float r) {x=a; y=b;}

Referring to this

If a lambda expression occurs inside a member function, it can refer to this. Because this is not a variable, it cannot be captured by reference. Even when it is captured implicitly in a lambda expression introduced by [&], it is captured by copy.