Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a special rule for lambda in case of decltype(auto)?

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)?

like image 896
W.F. Avatar asked Jul 06 '17 10:07

W.F.


1 Answers

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-specifierT shall be the placeholder alone. The type deduced for T is determined as described in [dcl.type.simple], as though e had been the operand of the decltype.

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...

like image 153
Rakete1111 Avatar answered Oct 05 '22 07:10

Rakete1111