[Example 2: void f(){int x =0;
auto g =[x](int x){return0; }; // error: parameter and capture have the same nameauto h =[y =0]<typename y>(y){return0; }; // error: template parameter and capture// have the same name} — end example]
if the capture is by copy (see below), the non-static data member
declared for the capture and the variable are treated as two different ways
of referring to the same object, which has the lifetime of the non-static
data member, and no additional copy and destruction is performed, and
This enables an init-capture like
“x = std::move(x)”; the second “x” must bind to a
declaration in the surrounding context.
— end note]
[Example 3: int x =4;
auto y =[&r = x, x = x+1]()->int{
r +=2;
return x+2;
}(); // Updates ::x to 6, and initializes y to 7.auto z =[a =42](int a){return1; }; // error: parameter and conceptual local variable have the same nameauto counter =[i=0]()mutable->decltype(i){// OK, returns intreturn i++;
};
— end example]
An id-expression that names a local entity
potentially references that entity;
an id-expression that names
one or more non-static class members
and does not form a pointer to member ([expr.unary.op])
potentially references *this.
If an expression potentially references a local entity
within a scope in which it is odr-usable ([basic.def.odr]),
and the expression would be potentially evaluated
if the effect of any enclosing typeid expressions ([expr.typeid]) were ignored,
the entity is said to be implicitly captured
by each intervening lambda-expression with an associated
capture-default that does not explicitly capture it.
[Example 4: void f(int, constint(&)[2]={}); // #1void f(constint&, constint(&)[1]); // #2void test(){constint x =17;
auto g =[](auto a){
f(x); // OK, calls #1, does not capture x};
auto g1 =[=](auto a){
f(x); // OK, calls #1, captures x};
auto g2 =[=](auto a){int selector[sizeof(a)==1?1:2]{};
f(x, selector); // OK, captures x, can call #1 or #2};
auto g3 =[=](auto a){typeid(a + x); // captures x regardless of whether a + x is an unevaluated operand};
}
Within g1, an implementation can optimize away
the capture of x as it is not odr-used.
The set of captured entities is determined syntactically,
and entities are implicitly captured
even if the expression denoting a local entity
is within a discarded statement ([stmt.if]).
[Example 5: template<bool B>void f(int n){[=](auto a){ifconstexpr(B &&sizeof(a)>4){(void)n; // captures n regardless of the value of B and sizeof(int)}}(0);
} — end example]
void f1(int i){intconst N =20;
auto m1 =[=]{intconst M =30;
auto m2 =[i]{int x[N][M]; // OK, N and M are not odr-used
x[0][0]= i; // OK, i is explicitly captured by m2 and implicitly captured by m1};
};
struct s1 {int f;
void work(int n){int m = n*n;
int j =40;
auto m3 =[this,m]{auto m4 =[&,j]{// error: j not odr-usable due to intervening lambda m3int x = n; // error: n is odr-used but not odr-usable due to intervening lambda m3
x += m; // OK, m implicitly captured by m4 and explicitly captured by m3
x += i; // error: i is odr-used but not odr-usable// due to intervening function and class scopes
x += f; // OK, this captured implicitly by m4 and explicitly by m3};
};
}};
}struct s2 {double ohseven =.007;
auto f(){return[this]{return[*this]{return ohseven; // OK};
}();
}auto g(){return[]{return[*this]{}; // error: *this not captured by outer lambda-expression}();
}};
— end example]
Because local entities are not
odr-usable within a default argument ([basic.def.odr]),
a lambda-expression appearing in a default argument
cannot implicitly or explicitly capture any local entity.
For each entity captured by copy, an
unnamed non-static data member is declared in the closure type.
The declaration order of
these members is unspecified.
The type of such a data member is
the referenced type if the entity is a reference to an object,
an lvalue reference to the referenced function type if the entity is a reference to a function, or
the type of the corresponding captured entity otherwise.
A member of an anonymous union shall not be captured by copy.
An id-expression that is not an odr-use refers to
the original entity, never to a member of the closure type.
However, such
an id-expression can still cause the implicit capture of the
entity.
— end note]
If *this is captured by copy, each expression that odr-uses *this is
transformed to instead refer to the corresponding unnamed data member of the closure type.
[Example 8: void f(constint*);
void g(){constint N =10;
[=]{int arr[N]; // OK, not an odr-use, refers to variable with automatic storage duration
f(&N); // OK, causes N to be captured; &N points to// the corresponding member of the closure type};
} — end example]
An entity is captured by reference if it is implicitly or explicitly
captured but not captured by copy.
It is unspecified whether additional unnamed
non-static data members are declared in the closure type for entities captured by
reference.
If declared, such non-static data members shall be of literal type.
[Example 9: // The inner closure type must be a literal type regardless of how reference captures are represented.static_assert([](int n){return[&n]{return++n; }(); }(3)==4);
— end example]
A bit-field or a member of an anonymous union
shall not be captured by reference.
An id-expression within
the compound-statement of a lambda-expression
that is an odr-use of a reference captured by reference
refers to the entity to which the captured reference is bound and
not to the captured reference.
The validity of such captures is determined by
the lifetime of the object to which the reference refers,
not by the lifetime of the reference itself.
— end note]
[Example 10: auto h(int&r){return[&]{++r; // Valid after h returns if the lifetime of the// object to which r is bound has not ended};
} — end example]
If a lambda-expressionm2 captures an entity and that entity is
captured by an immediately enclosing lambda-expressionm1, then
m2's capture is transformed as follows:
If m1 captures the entity by copy,
m2 captures the corresponding
non-static data member of m1's closure type;
if m1 is not mutable, the non-static data member is considered to be const-qualified.
int a =1, b =1, c =1;
auto m1 =[a, &b, &c]()mutable{auto m2 =[a, b, &c]()mutable{
std::cout << a << b << c;
a =4; b =4; c =4;
};
a =3; b =3; c =3;
m2();
};
a =2; b =2; c =2;
m1();
std::cout << a << b << c;
— end example]
When the lambda-expression is evaluated, the entities that are
captured by copy are used to direct-initialize each corresponding non-static data member
of the resulting closure object, and the non-static data members corresponding to the
init-captures are initialized as indicated by the corresponding
initializer (which may be copy- or direct-initialization).
(For array members, the array elements are
direct-initialized in increasing subscript order.)
These initializations are performed
in the (unspecified) order in which the non-static data members are declared.
If a non-reference entity is implicitly or explicitly captured by reference,
invoking the function call operator of the corresponding lambda-expression
after the lifetime of the entity has ended is likely to result in undefined behavior.