If I understood correctly this answer and referenced standard section [dcl.type.auto.deduct-5], the code:
decltype(auto) a = e;
is always equivalent to
decltype( e ) a = e;
But now the problem appears if instead of e
I put the lambda expression to decltype(auto)
:
decltype(auto) lambda = [](){};
This compiles, to my surprise, successfully in both gcc and clang. Reason for the shock I've experienced lays in standard which says specifically that lambda should not occur in unevaluated operand [expr.prim.lambda#2] (emphasis mine):
A lambda-expression is a prvalue whose result object is called the closure object. A lambda-expression shall not appear in an unevaluated operand, in a template-argument, in an alias-declaration, in a typedef declaration, or in the declaration of a function or function template outside its function body and default arguments.
But as I mentioned the example would be equivalent to:
decltype([](){}) lambda = [](){};
The above code written explicitly obviously would be ill-formed. Of course we could assume that the statement [](){}
inside decltype
is kind of reference that isn't really a reference like in case of structured bindings, but maybe there is a special rule in standard that I've missed covering lambda initializing decltype(auto)
?
This answer is based on my interpretation of the relevant Standard text. Those sections are not very clear with divided opinions, and thus it is currently hard to know the exact meaning of them. It seems that, excluding a possible oversight, the major compilers seem to agree that the definition in question is indeed well-formed.
In addition, it is my opinion that it would be very surprising to hear that the definition were ill-formed.
Reason for the shock I've experienced lays in standard which says specifically that lambda should not occur in unevaluated operand [...]
Where do you see that a lambda appears in an unevaluated context?
decltype(auto) lambda = [](){};
I don't see it, because there is none. The lambda is used as an initializer, which is completely legal.
Now your confusion probably comes about because you seem to think that the above statement is equivalent to
decltype([](){}) lambda = [](){};
That's not the case though, strictly speaking. If you look at the language of the wording, there is a small difference (highlighted by me):
If the placeholder is the
decltype(auto)
type-specifier,T
shall be the placeholder alone. The type deduced forT
is determined as described in [dcl.type.simple], as thoughe
had been the operand of thedecltype
.
The key word here is though. It just means that the deduction happens as if it were decltype(e)
, meaning that the deduction rules of decltype
apply instead of those for auto
for the operand e
.
Here, the operand e
is indeed the lambda, but that is completely legal, because the Standard mandates that the behavior is the same as if you would have written decltype([](){})
, meaning that of the rules of decltype
deduction apply for the lambda. Now [expr.prim.lambda]/2
doesn't apply here, because the lambda is not in an unevaluated context, so it is actually legal for the compiler to use decltype([](){})
to deduce the type, meaning that the decltype
rules have to be used for the lambda.
Sure, if you write decltype([](){})
, the program is ill-formed, but that is not the case here, as mentioned above.
In this case, because a lambda expression is a prvalue, the deduced type should just be the type of the lambda.
At least that's how I understand it...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With