Look at this example (godbolt):
void foo(int &par) {
auto l = [par]() {
decltype(par) x;
};
}
This program does not compile, because the par
in decltype(par)
refers to the parameter in foo
, and not to the variable of the lambda (so decltype(par)
is actually a reference type, which needs to be initialized).
Here is a relevant quote from the standard of C++14 (sorry about quoting an old standard, but I think it's easier to understand).
expr.prim.lambda/18 (emphasis is mine):
"Every id-expression within the compound-statement of a lambda-expression that is an odr-use of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type. [ Note: An id-expression that is not an odr-use refers to the original entity, never to a member of the closure type. Furthermore, such an id-expression does not cause the implicit capture of the entity. — end note ]"
And the standard makes sure that if decltype used on a non-id-expression, then even tough it doesn't odr-use the variable, the captured one is used (expr.prim.lambda/19):
"Every occurrence of decltype((x)) where x is a possibly parenthesized id-expression that names an entity of automatic storage duration is treated as if x were transformed into an access to a corresponding data member of the closure type that would have been declared if x were an odr-use of the denoted entity."
So, depending on these rules (and maybe the standard has other related rules), a variable in a lambda may refer to the captured one, or may refer to the original one in the enclosing function.
I have two questions:
- Why is it designed this way? Why not always use the captured variable? What does this extra complexity give us?
- How can I acquire the type of a captured variable inside a lambda?
(Note: a related previous question: What is the type of a by-value lambda capture of a reference?)
decltype(auto(par))
which is what most of us wanted to have for 10+ years, I think. – Absencedecltype(auto(x))
is always an object, even ifx
is captured by reference. I suspect there is no way to get the type of the captured entity, and just2.
would be a good question on its own. – Inflatedpar
was odr-used and captured. So if they want an object, it's useful to tell them how to spell "an object". – Absencepar
was captured by reference,auto(par)
is still going to be an object. It's not the type of the captured variable, and could just as well be written asstd::remove_reference_t<decltype(par)>
. I'm just trying to say that there should be a disclaimer that usingauto
is not a general solution. – Inflateddecltype(auto(x))
does not adequately solve that. It doesn't get the type of the lambda member generally, just of the one in the example. Simply writingint
would also be a viable solution to that problem. – Inflatedint x;
local variable, and then don't odr use it, I can still saydecltype(x)
, and it will beint
. – Avastx
is still "there" in the abstract machine. In the case of a lambda capture, it may not exist in the abstract machine at all, to my reading. I don't know why that is, though. I can't really answer why it's like that, only observe it. – Absence