在C ++ 11中用“ auto”推论得出的lambda类型是什么?


141

我认为lambda的类型是函数指针。当我执行以下测试时,我发现它是错误的(演示)。

#define LAMBDA [] (int i) -> long { return 0; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok
  assert(typeid(pFptr) == typeid(pAuto));  // assertion fails !
}

上面的代码缺少任何意义吗?如果不是,那么typeofauto关键字推导的lambda表达式是什么?


8
“ lambda的类型是函数指针” –效率低下,会错过lambda的全部要点。
康拉德·鲁道夫

Answers:


144

Lambda表达式的类型未指定。

但它们通常仅是函子的语法糖。Lambda直接转换为函子。内的所有内容[]都将转换为构造函数参数和functor对象的成员,而内部的参数()都将转换为functor的参数operator()

不能捕获任何变量的lambda([]s中的任何内容)都不能转换为函数指针(如果是编译器,则MSVC2010不支持此指针,但是此转换是标准的一部分)。

但是lambda的实际类型不是函数指针。这是一些未指定的函子类型。


1
MSVC2010不支持转换为函数指针,但MSVC11支持。blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
KindDragon 2011年

17
+1表示“仅用于函子的语法糖”。记住这一点可以避免很多潜在的混乱。


107

这是一个独特的未命名结构,使函数调用运算符过载。每个lambda实例都会引入一个新类型。

在非捕获lambda的特殊情况下,该结构还具有对函数指针的隐式转换。


2
好答案。比我的精确得多。+1 :)
jalf

4
+1是唯一性部分,起初非常令人惊讶,值得关注。
Matthieu M.

并不是真的很重要,但是类型真的没有命名,还是只是在编译时才给它命名?IOW,可以使用RTTI查找编译器决定的名称吗?

3
@Ben,它是未命名的,就C ++语言而言,没有“编译器决定的名称”之类的东西。结果type_info::name()是实现定义的,因此它可能返回任何内容。实际上,编译器将为链接器命名类型。
avakar,2011年

1
最近,当被问到这个问题时,我通常会说lambda的类型一个名称,编译器知道它的名字,这简直是难以置信的。
安德烈·科斯特

24

[C++11: 5.1.2/3]: 的类型的λ-表达(这也是封闭的对象的类型)是一个独特的,无名不愈合类类型 -称为闭合类型 -其特性如下所述。此类不是聚合(8.5.1)。在包含相应的lambda-expression的最小块范围,类范围或名称空间范围中声明闭包类型。[..]

该子句继续列出了此类型的各种属性。以下是一些要点:

[C++11: 5.1.2/5]:lambda-expression的闭包类型具有一个公共inline函数调用运算符(13.5.4),其参数和返回类型由lambda-expression parameter-declaration-clauseTrailing-return-type描述[..]

[C++11: 5.1.2/6]: 一个的关闭类型 没有lambda-capture lambda表达式具有指向该函数的指针的公共非虚拟非显式const转换函数,该函数具有与闭包类型的函数调用运算符相同的参数和返回类型。该转换函数返回的值应是一个函数的地址,该函数在被调用时与调用闭包类型的函数调用运算符具有相同的作用。

最后一段的结果是,如果您使用了转换,则可以将分配LAMBDApFptr


2
#include <iostream>
#include <typeinfo>

#define LAMBDA [] (int i)->long { return 0l; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok

  std::cout<<typeid( *pAuto ).name() << std::endl;
  std::cout<<typeid( *pFptr ).name() << std::endl;

  std::cout<<typeid( pAuto ).name() << std::endl;
  std::cout<<typeid( pFptr ).name() << std::endl;
}

函数类型的确相同,但是lambda引入了新的类型(如函子)。


如果您已经走过这条路线,我建议您使用CXXABI取消管理方法。取而代之的是,我通常使用中的__PRETTY_FUNCTION__,如中所述template<class T> const char* pretty(T && t) { return __PRETTY_FUNCTION__; },并在多余的东西开始变得拥挤时将其剥离。我更喜欢看模板替换中显示的步骤。如果您缺少__PRETTY_FUNCTION__,则可以使用MSVC等替代方法,但是出于同样的原因,结果是CXXABI是必需的,因此结果始终取决于编译器。
John P

1

还应注意,lambda可转换为函数指针。但是,typeid <>返回一个非从属对象,该对象从lambda到泛型函数指针应有所不同。因此,对typeid <>的测试不是有效的假设。通常,C ++ 11不想让我们担心类型说明,如果给定类型可转换为目标类型,那么这一切都无所谓。


这是很公平的,但是打印类型在走向正确的类型方面还有很长的路要走,更不用说捕捉类型可以转换但不满足其他约束的情况了。(我会尽可能地推动“调整”约束,但是尝试这样做的人更有理由在开发过程中展示其工作。)
John P

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.