Consider the simple program:
int i = 0;
int& j = i;
auto lambda = [=]{
std::cout << &j << std::endl; //odr-use j
};
According to [expr.prim.lambda], the closure member variable j
should have type int
:
An entity is captured by copy if it is implicitly captured and the capture-default is
=
or if it is explicitly captured with a capture that is not of the form & 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 type of the corresponding captured entity if the entity is not a reference to an object, or the referenced type otherwise.
So what I'm printing is the address of some int
unrelated to the outer-scope i
or j
. This is all well 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: !
};
That fails to compile because decltype(j)
evaluates as int&
. Why? j
in that scope should refer to the data member, should it not?
As a related followup, if the lambda capture were instead an init-capture with [j=j]{...}
, then clang would report decltype(j)
as int
and not int&
. Why the difference?
The way name lookup works inside lambda-expressions is a bit peculiar: id-expressions which refer to entities captured by copy are transformed from accesses to the captured entities to accesses to the stored data members of the closure type -- but only if these accesses constitute odr-uses. Note that due to implicit capture, if there's no odr-use, there is possibly no such data member.
decltype
does not constitute an odr-use, hence it will always refer to the captured entity (the original), not the data member (the copy).C++11 [expr.prim.lamba]p17
and furthermore, p18 even displays this weird effect in an example:
The C++14 init-captures are also considered capture by copy, since C++14 [expr.prim.lambda]p15
However, as T.C. has pointed out, they do not capture the entity they've been initialized with, but rather a "dummy variable" which is also used for type deduction [expr.prim.lambda]p11
The type deduction alters the type of this variable, e.g.
char const[N]
->char const*
, and the original entity might not even have a type, e.g.[i = {1,2,3}]{}
.Therefore, the id-expression
j
in the lambda[j=j]{ decltype(j) x; }
refers to this dummy variable and its type isint
, notint&
.