[expr.prim.lambda.capture] (original) (raw)

7 Expressions [expr]

7.5 Primary expressions [expr.prim]

7.5.5 Lambda expressions [expr.prim.lambda]

7.5.5.2 Captures [expr.prim.lambda.capture]

lambda-capture: capture-default capture-list capture-default , capture-list

capture-default: & =

capture-list: capture capture-list , capture

capture: simple-capture init-capture

simple-capture: identifier ... & identifier ... this * this

init-capture: ... identifier initializer & ... identifier initializer

The body of a lambda-expression may refer to variables with automatic storage duration and the *this object (if any) of enclosing block 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 thatlambda-capture shall be preceded by &.

If a lambda-capture includes acapture-default that is =, eachsimple-capture of that lambda-capture shall be of the form “& identifier ...”, “this”, or “* this”.

[ Note

:

The form [&,this] is redundant but accepted for compatibility with ISO C++ 2014.

end note

]

Ignoring appearances ininitializers of init-captures, an identifier orthis shall not appear more than once in alambda-capture.

[ Example

:

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 * thisdenote the local entity *this.

An entity that is designated by asimple-captureis said to be explicitly captured.

[ Example

:

void f() { int x = 0; auto g = [x](int x) { 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 ;” whose declarative region is the lambda-expression'scompound-statement, except that:

[ Note

:

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

:

int x = 4; auto y = &r = x, x = x+1->int { r += 2; return x+2; }();

auto z = [a = 42](int a) { return 1; };

end example

]

For the purposes of lambda capture, an expression potentially references local entities as follows:

If an expression potentially references a local entity within a declarative region in which it is odr-usable, 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 capturedby each intervening lambda-expression with an associatedcapture-default that does not explicitly capture it.

The implicit capture of *this is deprecated when thecapture-default is =; see [depr.capture.this].

[ Example

:

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 might optimize away the capture of x as it is not odr-used.

end example

]

[ Note

:

The set of captured entities is determined syntactically, and entities might be implicitly captured even if the expression denoting a local entity is within a discarded statement ([stmt.if]).

[ Example

:

template 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

:

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

:

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

:

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

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

:

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

:

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

:

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-expressionthat 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

:

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

:

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, thenm2's capture is transformed as follows:

[ Example

:

The nested lambda-expressions and invocations below will output123234.

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 theinit-captures are initialized as indicated by the correspondinginitializer (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

:

This ensures that the destructions will occur in the reverse order of the constructions.

end note

]

[ Note

:

If a non-reference entity is implicitly or explicitly captured by reference, invoking the function call operator of the corresponding lambda-expressionafter the lifetime of the entity has ended is likely to result in undefined behavior.

end note

]

[ Example

:

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

]