From cppinsights we see how in the following code the line lambda();
is interpreted by the language:
const auto lambda = [] () mutable {};
void foo()
{
lambda();
}
One would naively think, that this line calls the lambda's (non-const) operator()
, which in turn doesn't compile. But, instead of this, the compiler converts the non-capturing lambda to a function pointer, calls the function pointer, and accepts the code.
What's the purpose of this conversion? To me, it would be more logical, if this would be rejected. There is no sign, that the programmer intended this conversion. The language does this on its own.
Note, that the conversion happens only in the above case, where calling lambda.operator()()
would discard qualifiers. It does not happen (i.e., operator()
is called directly), if lambda
is not const
, or operator()
is not marked mutable
.
Let Lambda
be the type of the lambda closure. Thus decltype(lambda)
is const Lambda
.
The expression lambda()
can be interpreted in several ways. It can be – in this case –
Lambda::operator()
, orlambda
. Since the capture list of the lambda expression is empty, a conversion function to void (*)()
exists (see the rule). We can see it in cppinsights too.This is detailed in overload resolution:
If
E
in a function call expressionE(args)
has class type cvT
, then
The function-call operators of
T
are obtained by ordinary lookup of the nameoperator()
in the context of the expression(E).operator()
, and every declaration found is added to the set of candidate functions.For each non-explicit user-defined conversion function in
T
or in a base ofT
(unless hidden), whose cv-qualifiers are the same or greater thanT
's cv-qualifiers, and where the conversion function converts to:
- pointer-to-function
- reference-to-pointer-to-function
- reference-to-function
then a surrogate call function with a unique name whose first parameter is the result of the conversion, the remaining parameters are the parameter-list accepted by the result of the conversion, and the return type is the return type of the result of the conversion, is added to the set of candidate functions. If this surrogate function is selected by the subsequent overload resolution, then the user-defined conversion function will be called and then the result of the conversion will be called.
In this case in E(args)
E
is lambda
, args
is empty, and cv T
is const Lambda
, a class type. For this expression there are two candidates in contest:
void Lambda::operator()()
, the non-const function-call operator of the lambda closure, andvoid SurrogateFunction(void(*)())
, the invented surrogate function.Let's rewrite the first candidate in the form of a global function, to make it easier to understand. In this case its first parameter will be the object *this
:
void FunctionCallOperator(Lambda&)
,void SurrogateFunction(void(*)())
.The lvalue expression lambda
of type const Lambda
is tried in these two.
Lambda
.So, there is no hidden purpose behind what's happening. The conversion function wins the overload resolution, which is a general rule, not specific to lambdas.