Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function signature differences in C++11

Considering C++11's lambdas with the following code,

template <typename M>
void call(void (*f)(M), M m)
{
  f(m);
}

int main()
{
  call<int>([](int n) { }, 42);          // OK

  int r;
  call<int>([&](int n) { r = n; }, 42);  // KO
}

is there a signature difference between the lambdas that makes the second one incompatible with the argument of call?

I use g++ 4.6.1.

Side question: why can't the parameter be inferred if I write call([](int n) { }, 42);?

like image 429
Warren Seine Avatar asked Apr 07 '11 21:04

Warren Seine


2 Answers

Only a captureless lambda can be implicitly converted to function pointer.

A lambda that captures variables cannot be converted to a function pointer because it has state that needs to be maintained (the captured variables) and that state cannot be represented by a function pointer.

The type M cannot be inferred from the function arguments because a conversion is required to convert the lambda to a function pointer. That conversion inhibits template argument deduction. If you were to call the function call with an actual function (e.g., void f(int)), argument deduction would work just fine.

like image 139
James McNellis Avatar answered Sep 22 '22 15:09

James McNellis


As James already answered, only captureless lambdas can be converted to function pointers. Lambdas that have state create functor objects that implement operator(), and member function pointers are incompatible with free function pointers.

When the compiler processes: [&](int n){ r = n; } it generates something like:

class __annonymous_lambda_type {
   int & r;
public:
   __annonymous_lambda_type( int & r ) : r(r) {}
   void operator()( int n ) const {
      r = n; 
   }
} __lambda_instatiation;

The class is required to store the state of the lambda, in this case the reference to the external object that will be modified when the lambda is executed. That void operator()(int) cannot be bound to a void (*)(int).

On the other hand, if the lambda is stateless, it can be implemented as a free function, like in the case []( int n ) { std::cout << "Hi" << n << std::endl ; }

void __annonymous_lambda_function( int n ) {
   std::cout << "Hi " << n << std::endl;
}

Because the lambda does not need to keep any state at all, and as such it can be kept as a plain function.

like image 29
David Rodríguez - dribeas Avatar answered Sep 22 '22 15:09

David Rodríguez - dribeas