Here is the full example:
auto callSelf = [](auto& func) {func(func);};
class wrapper : public decltype(callSelf) {
using base = decltype(callSelf);
public:
wrapper() : base(callSelf) {}
template<class T>
void operator()(T& func) {
base::operator()(func);
}
};
int main()
{
//callSelf(callSelf); // Error
wrapper w;
w(w); // OK, nice endless recursion
}
Why is it possible with the wrapper, while doing it directly causes the following error?
main.cpp:30: error: use of '<lambda(auto:1&)> [with auto:1 = <lambda(auto:1&)>]' before deduction of 'auto'
auto callSelf = [&](auto& func) {func(func);};
~~~~^~~~~~
This is actually quite tricky. The rule you're running afoul of is in [dcl.spec.auto]:
If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression, the program is ill-formed.
That is what's going wrong here:
auto callSelf = [](auto& func) {func(func);};
callSelf(callSelf);
We need to know the type of callSelf
to determine the type of the expression of func(func)
, which it iself circular. This is easily resolvable by simply specifying the return type:
auto callSelf = [](auto& func) -> void {func(func);};
callSelf(callSelf); // ok. I mean, infinite recursion, but otherwise ok. ish.
However, when you wrap the lambda, you get different behavior. This line here:
w(w);
is passing an object of type wrapper
into, effectively, the lambda. That is not its own type. The body of the lambda invokes that object on itself, but we know the type of that expression. You declared it:
template<class T>
void operator()(T& func) {
~~~~~
This function works (for some definition of works) with void
for the same reason the lambda worked when we added -> void
. It's no longer an undeduced placeholder. We already know the return type. To get the same behavior as with the lambda, change the declaration of operator()
to be auto
.
In your case, simply define the return type and the compiler should accept it:
auto callSelf = [](auto& func) -> void {func(func);};
class wrapper : public decltype(callSelf) {
using base = decltype(callSelf);
public:
wrapper() : base(callSelf) {}
template<class T>
void operator()(T& func) {
base::operator()(func);
}
};
int main()
{
callSelf(callSelf); //works
wrapper w;
w(w); //ok, nice endless recursion
}
With return type deduction, the compiler cannot use the lambda in the lambda itself because the compiler has to see the body of the function to deduce the return type. The fact that the compiler has to check the body of the function make it see the content of your lambda that uses the lambda itself. Since the compiler is in the deduction process, you cannot use the lambda, hence the compilation error.
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