Lambda record using copy and decltype

Consider a simple program:

int i = 0; int& j = i; auto lambda = [=]{ std::cout << &j << std::endl; //odr-use j }; 

According to [expr.prim.lambda], the j closure variable must be of type int :

An object is captured by a copy if it is implicitly fixed, and the default value is = or if it is explicitly captured with a capture that does not have a form identifier and an identifier and / or identifier. For each object captured by the copy, an unnamed non-static data element is declared in the close type. The order of declaration of these members is not specified. The type of such a data element is the type of the corresponding captured object, unless the object is a reference to an object, or a reference type otherwise .

So, what I'm printing is the address of some int , not related to the outer scope i or j . This is all good and good. However, when I throw in decltype :

 auto lambda = [j] { std::cout << &j << std::endl; static_assert(std::is_same<decltype(j), int>::value, "!"); // error: ! }; 

This does not compile because decltype(j) evaluates to int& . What for? j in this area should refer to a data element if it is not?

As a related continuation, if lambda capture was instead of init-capture with [j=j]{...} , then clang would decltype(j) as int , not int& . Why is the difference?

+5
source share
1 answer

The method of searching for names inside lambda expressions is a little peculiar: id expressions that refer to objects captured by a copy are converted from access to captured objects to access stored data items such as closures - but only if these calls are uses odr . Note that due to implicit capture, if there is no use of odr, there may not be such a data element.

decltype not an odr-use, so it will always refer to the captured object (the original), and not to the data item (copy).

C ++ 11 [expr.prim.lamba] p17

Each id-expression, which is an odr-use of an object that is captured, is converted to a member of the closure type to access the corresponding unnamed data.

and besides, p18 even displays this strange effect in the example:

Each occurrence of decltype((x)) , where x possibly possible in parentheses, an id expression that names the entity of automatic storage was processed as if x were converted to access the corresponding data member of the closure type, which would be declared if x were one-way using a designated entity. [Example:

 void f3() { float x, &r = x; [=] { // x and r are not captured (appearance in a decltype operand is not an odr-use) decltype(x) y1; // y1 has type float decltype((x)) y2 = y1; // y2 has type float const& because this lambda // is not mutable and x is an lvalue decltype(r) r1 = y1; // r1 has type float& (transformation not considered) decltype((r)) r2 = y2; // r2 has type float const& }; } 

- end of example]


The initial C ++ 14 entries are also considered a capture copy, since C ++ 14 [expr.prim.lambda] p15

An entity is captured by the copy if it is implicitly captured, and capture-default - = , or if it is explicitly captured by a capture that is not the form identifier & or the initializer of the & identifier.

However, as TC noted, they do not commit the object with which they were initialized, but rather a "dummy variable", which is also used for the deduction type [expr.prim.lambda] p11

The execution of init-capture behaves as if it declares and explicitly captures a variable of the form " auto init-capture ; ", the declarative area of ​​which is a composite operator of lambda expressions [...]

The type of output changes the type of this variable, for example. char const[N] β†’ char const* , and the source object may not even have a type, for example. [i = {1,2,3}]{} .

Therefore, the id expression j in lambda [j=j]{ decltype(j) x; } [j=j]{ decltype(j) x; } refers to this dummy variable, and its type is int , not int& .

+9
source

Source: https://habr.com/ru/post/1232744/


All Articles