C ++ 11中的函数签名差异

时间:2011-04-07 21:32:30

标签: c++ lambda c++11 g++ capture

使用以下代码考虑C ++ 11的lambdas,

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
}

lambda之间是否存在签名差异,使第二个与call的参数不兼容?

我使用g ++ 4.6.1。

附带问题:如果我写call([](int n) { }, 42);,为什么不能推断出参数?

2 个答案:

答案 0 :(得分:15)

只有无捕获的lambda可以隐式转换为函数指针。

捕获变量的lambda无法转换为函数指针,因为它具有需要维护的状态(捕获的变量),并且该状态不能由函数指针表示。

无法从函数参数推断类型M,因为需要转换才能将lambda转换为函数指针。该转换禁止模板参数推断。如果您使用实际函数(例如call)调用函数void f(int),则参数推导可以正常工作。

答案 1 :(得分:7)

正如詹姆斯已经回答的那样,只有无法捕获的lambdas可以转换为函数指针。具有状态的Lambda创建实现operator()的仿函数对象,并且成员函数指针与自由函数指针不兼容。

当编译器处理:[&](int n){ r = n; }时,它会生成如下内容:

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

该类需要存储lambda的状态,在这种情况下是对执行lambda时将被修改的外部对象的引用。 void operator()(int)无法绑定到void (*)(int)

另一方面,如果lambda是无状态的,它可以实现为自由函数,就像[]( int n ) { std::cout << "Hi" << n << std::endl ; }

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

因为lambda根本不需要保持任何状态,因此它可以保持为普通函数。