-
Notifications
You must be signed in to change notification settings - Fork 8
Description
Full name of submitter: Anoop S. Rana
Reference (section label): [expr.prim.lambda.capture]
Link to reflector thread: https://stackoverflow.com/q/79878786/12002570
Issue description: Consider the definition of var in the below program that shows implementation divergence. GCC and Clang rejectes it while MSVC accepts it. Live demo
struct C
{
C(int) = delete;
C(){};
};
decltype([b = C(3)](){ return 4;}()) var; //msvc OK but gcc and clang rejects
decltype([C(3)](){ return 4;}()) var2; //accepted by all compilers
int main()
{
}
The program is well-formed as per current wording as explained in the linked thread and for completeness here again below:
The program is well-formed and msvc is right to accept it because the the operand of decltype is an unevaluated operand which means here we're in unevaluated context and so the lambda is never actually evaluated. This is explained in detail below.
First, from expr.prim.lambda.capture#10:
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 referenced type if the entity is a reference to an object, an lvalue reference to the referenced function type if the entity is a reference to a function, or the type of the corresponding captured entity otherwise.
A member of an anonymous union shall not be captured by copy.
This means that an unnamed non-static data member of type C will be declared in the closure type. Now, we move onto the initialization of this unnamed data member. From expr.prim.lambda.capture#15:
When the lambda-expression is evaluated, the entities that are captured by copy are used to direct-initialize each corresponding non-static data member of the resulting closure object, and the non-static data members corresponding to the init-captures are initialized as indicated by the corresponding initializer (which may be copy- or direct-initialization).
(For array members, the array elements are direct-initialized in increasing subscript order.) These initializations are performed in the (unspecified) order in which the non-static data members are declared.
Note the emphasis on "evaluated" in the above quoted reference. Here, the operand [b = C(3)](){ return 4;}() of the decltype specifier is never evaluated as it appears as an unevaluated operand which means that [expr.prim.lambda.capture#15] quoted above is not applicable. This can be seen from dcl.type:
The operand of the decltype specifier is an unevaluated operand.
Finally, expr.context:
In some contexts, unevaluated operands appear ([expr.prim.req], [expr.typeid], [expr.sizeof], [expr.unary.noexcept], [dcl.type.decltype], [temp.pre], [temp.concept]).
An unevaluated operand is not evaluated.
This means that the program is well-formed.
Essentially, since overload resolution is part of initilaization which itself won't be done here because the "When the lambda-expression is evaluated" isn't satisfied. That is, no initialization of the non-static data member so no question of selection of a deleted function. This is my reading of the standard. But it may not be the intended behavior. Kindly, clarify if =delete function can be used(misused?) this way.
Note that there is also dcl.fct.def.delete#2 in the standard that is intened to make this ill-formed:
A construct that designates a deleted function implicitly or explicitly, other than to declare it or to appear as the operand of a reflect-expression ([expr.reflect]), is ill-formed..