Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda returning lambda incorrectly infers return type?

This question is a continuation of a recent question of mine:
What is this compiler error when using a lambda as a template parameter?

Nov. 11, 2014: Microsoft has responded saying the fix for this bug should show up in the next major release of Visual C++.


This code fails to compile using the VS2012 (Update 2):

int main(int argc, char* argv[])
{
    auto f = []()
    {
        int n = 0;
        auto r = [=]{ return n; };
        return r;
    };
    return 0;
}

This is the compiler error I get:

1>  main.cpp
1>C:\test\main.cpp(7): error C2440: 'return' : cannot convert from 'main::<lambda_c5d1d707b91a1ddedc06eb080503550c>::()::<lambda_ac357c309731f4971c3269160ed9c24b>' to 'int (__cdecl *)(void)'
1>          No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

  • Is there a problem with the code according to C++11 specification?
  • Is there a problem with the code according to VS2012's defined partial C++11 support?
  • Or is this a VS2012 C++ compiler bug?

  • Could someone point me to the place in the C++11 specification that talks about how lambdas must be implicitly castable to function pointers?
    • I recall this only being for stateless lambdas - those with empty capture clauses - which the inner lambda r is not
    • So why does it appear that the inferred return type of lambda f is a function pointer, namely int (__cdecl *)(void)?
like image 543
Timothy Shields Avatar asked Nov 03 '22 21:11

Timothy Shields


1 Answers

Although GCC 4.7.2 compiles this code it's ill formed. The lambda expression which initializes f is too complex for deducing the return type. Indeed, 5.1.2/4 says

If a lambda-expression does not include a trailing-return-type, it is as if the trailing-return-type denotes the following type:

— if the compound-statement is of the form

   { attribute-specifier-seq[opt] return expression ; }

the type of the returned expression after lvalue-to-rvalue conversion (4.1), array-to-pointer conversion (4.2), and function-to-pointer conversion (4.3);

— otherwise, void.

Therefore, in this example the return type is void but the lambda is returning something else. The code should not compile.

I agree that the message given by Visual Studio is misleading.

Update: On this question

So would it be correct to say "In C++11, you cannot define a lambda that returns a stateful lambda"?

No. As per the C++11 quote below, the type returned by a lambda is void unless the body of the lambda contains just a single line with a return expression;. Hence, if you manage to create your stateful lambda in a return expression, then this is fine. For instance, the code below compiles in GCC 4.7.2, Clang 3.2 and Intel compiler 13.1.0: (It doesn't compile in VS2012 due to the aforementioned bug.)

#include <iostream>

int main() {
  int n = 5;
  auto f = [=] {
      return [=]{ return n; }; // creates a stateful lambda and returns it in a single line
  };
  std::cout << f()() << std::endl;
  return 0;
}
like image 97
Cassio Neri Avatar answered Nov 09 '22 13:11

Cassio Neri