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

War*_*ine 6 c++ lambda g++ capture c++11

考虑使用以下代码的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
}
Run Code Online (Sandbox Code Playgroud)

lambda之间是否存在签名差异,使得第二个与参数不相符call

我用的是g ++ 4.6.1.

旁边的问题:如果我写的话为什么不能推断参数call([](int n) { }, 42);

Jam*_*lis 15

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

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

M无法从函数参数推断出类型,因为需要转换才能将lambda转换为函数指针.该转换禁止模板参数推断.如果你call用一个实际的函数调用函数(例如void f(int)),参数推导就可以了.

  • 注意,"M"由第二个参数推导出来.因此,为了使这项工作,我们可以在第一个参数中创建一个非推导的上下文.`void call(void(*f)(typename identity <M> :: type),M m)`.现在,C++ 0x需要这项工作.一些当前的编译器将要求整个参数是非推断的上下文(comeau不是,但是GCC和clang afaik确实如此).因此,为了增加兼容性,可以说`void call(typename identity <void(*f)(M)> :: type,M m)`.由于它是非推断的上下文,因此不会进行比较,也不会导致演绎失败. (7认同)

Dav*_*eas 7

正如詹姆斯已经回答的那样,只有无法捕获的lambdas可以转换为函数指针.具有状态的Lambdas创建实现的functor对象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;
Run Code Online (Sandbox Code Playgroud)

该类需要存储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;
}
Run Code Online (Sandbox Code Playgroud)

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