[expr.prim.lambda.capture]
7 Expressions [expr]
7.5 Primary expressions [expr.prim]
7.5.6 Lambda expressions [expr.prim.lambda]
7.5.6.3 Captures [expr.prim.lambda.capture]
The body of a lambda-expression may refer to local entities of enclosing scopes by capturing those entities, as described below.
If a lambda-capture includes a capture-default that is &, no identifier in a simple-capture of that lambda-capture shall be preceded by &.
If a lambda-capture includes a capture-default that is =, each simple-capture of that lambda-capture shall be of the form “& identifier ...”, “this”, or “* this”.
Ignoring appearances in initializers of init-captures, an identifier or this shall not appear more than once in a lambda-capture.
[Example 1: struct S2 { void f(int i); }; void S2::f(int i) { [&, i]{ }; [&, this, i]{ }; [&, &i]{ }; [=, *this]{ }; [=, this]{ }; [i, i]{ }; [this, *this]{ }; } — end example]
The simple-captures this and * this denote the local entity *this.
An entity that is designated by a simple-capture is said to be explicitly captured.
[Example 2: void f() { int x = 0; auto g = [x](int x) { return 0; }; auto h = [y = 0]<typename y>(y) { return 0; }; } — end example]
An init-capture without ellipsis behaves as if it declares and explicitly captures a variable of the form “auto init-capture ;”, except that:
- 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
- if the capture is by reference, the variable's lifetime ends when the closure object's lifetime ends.
[Note 2:
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; }(); auto z = [a = 42](int a) { return 1; }; auto counter = [i=0]() mutable -> decltype(i) { return i++; }; — end example]
For the purposes of lambda capture, an expression potentially references local entities as follows:
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.
[Note 3:
This occurs even if overload resolution selects a static member function for the id-expression.
— end note]
A this expression 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.
The implicit capture of *this is deprecated when the capture-default is =; see [depr.capture.this].
[Example 4: void f(int, const int (&)[2] = {}); void f(const int&, const int (&)[1]); void test() { const int x = 17; auto g = [](auto a) { f(x); }; auto g1 = [=](auto a) { f(x); }; auto g2 = [=](auto a) { int selector[sizeof(a) == 1 ? 1 : 2]{}; f(x, selector); }; auto g3 = [=](auto a) { typeid(a + x); }; }
Within g1, an implementation can optimize away the capture of x as it is not odr-used.
— end example]
[Note 4:
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) { if constexpr (B && sizeof(a) > 4) { (void)n; } }(0); } — end example]
— end note]
An entity is captured if it is captured explicitly or implicitly.
[Example 6: void f1(int i) { int const N = 20; auto m1 = [=]{ int const M = 30; auto m2 = [i]{ int x[N][M]; x[0][0] = i; }; }; struct s1 { int f; void work(int n) { int m = n*n; int j = 40; auto m3 = [this,m] { auto m4 = [&,j] { int x = n; x += m; x += i; x += f; }; }; } }; } struct s2 { double ohseven = .007; auto f() { return [this] { return [*this] { return ohseven; }; }(); } auto g() { return [] { return [*this] { }; }(); } }; — end example]
[Note 6:
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.
— end note]
[Example 7: void f2() { int i = 1; void g1(int = ([i]{ return i; })()); void g2(int = ([i]{ return 0; })()); void g3(int = ([=]{ return i; })()); void g4(int = ([=]{ return 0; })()); void g5(int = ([]{ return sizeof i; })()); void g6(int = ([x=1]{ return x; })()); void g7(int = ([x=i]{ return x; })()); } — end example]
An entity is captured by copy if
- it is implicitly captured, the capture-default is =, and the captured entity is not *this, or
- it is explicitly captured with a capture that is not of the form this, & identifier ..., or & ... identifier initializer.
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.
[Note 7:
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(const int*); void g() { const int N = 10; [=] { int arr[N]; f(&N); }; } — 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: 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.
[Note 8:
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; }; } — end example]
If a lambda-expression m2 captures an entity and that entity is captured by an immediately enclosing lambda-expression m1, then m2's capture is transformed as follows:
[Example 11:
The nested lambda-expressions and invocations below will output 123234.
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.
[Note 10:
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.
— end note]
An init-capture containing an ellipsis is a pack expansion that declares an init-capture pack ([temp.variadic]).
[Example 12: template<class... Args> void f(Args... args) { auto lm = [&, args...] { return g(args...); }; lm(); auto lm2 = [...xs=std::move(args)] { return g(xs...); }; lm2(); } — end example]