How can this lambda with an empty capture list refer to a scope name?

In the C ++ 14 standard ยง 5.1.2 / 12, it shows an example of a lambda expression, which, apparently, can refer to a scope variable x , although:

  • the capture list is empty, i.e. no default
  • The comment says it "does not commit x "

Here is an example:

 void f(int, const int (&)[2] = {}) { } // #1 void test() { const int x = 17; auto g = [](auto a) { f(x); // OK: calls #1, does not capture x }; } 

See what compiles . It seems to be on x const ; if const removed, it will no longer compile for reasons that might be expected (the capture list is empty). This happens even if I make the int parameter so that it is no longer a common lambda.

How can lambda refer to x even if the capture list is empty? And how is this possible, at the same time, apparently without capturing x (as the comment says)?

The closest thing I found on this topic was someone else tangentially noticing this in a comment.

Here is the full section 5.1.2 / 12 of the standard:

A lambda expression with an associated default capture that does not explicitly capture this or a variable with automatic storage duration (this excludes any identification expression that has been found to refer to non-static member data associated with init capture) is called an implicit object capture ( i.e. this or variable), if the compound statement:

  • odr uses (3.2) an object or
  • denotes an object in the potentially computed expression (3.2), where the encompassing full expression depends on the general parameter of the lambda declared within the scope of the lambda expression.

[Example:

 void f(int, const int (&)[2] = {}) { } // #1 void f(const int&, const int (&)[1]) { } // #2 void test() { const int x = 17; auto g = [](auto a) { f(x); // OK: calls #1, does not capture x }; auto g2 = [=](auto a) { int selector[sizeof(a) == 1 ? 1 : 2]{}; f(x, selector); // OK: is a dependent expression, so captures x }; } 

-end example] All such implicitly captured objects must be declared within the scope of the lambda expression. [Note. Implicit capture of an object by a nested lambda expression can cause it to be implicitly captured using the containing lambda expression (see below). Implicit use of odr of this can lead to implicit capture. -end note]

+6
source share
1 answer

You have the right quote. A variable must be captured if used by odr. ODR usage basically means that a variable is used in a context where a definition is required. Thus, either his address is taken, or refers to him, etc. One of the key exceptions is [basic.def.odr]:

The variable x , whose name is displayed as a potentially evaluated expression ex , is odr-used by ex , if the conversion lvalue-to-rvalue (4.1) to x is not applied, gives a constant expression (5.20) that does not call any non-trivial functions, and if x - the object, ex is an element of the set of potential results of the expression e , where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is an expression with a discarded value (paragraph 5).

So, in your example, applying the lvalue-to-rvalue to x conversion yields a constant expression (since x is a constant integral), so it is not used by odr. Since it is not used in odr, it does not need to be captured.

On the other hand, if x were bound to a link (for example, f took its argument as const int& ), then it would be used as odr, and therefore it would have to be captured. In the second example presented, x "odr-use-ness" depends on what is the common argument of the lambda, so that it is considered captured anyway for the sake of sanitation.

+10
source

All Articles