在 C++11 中用“auto”推导时 lambda 的类型是啥?

Posted

技术标签:

【中文标题】在 C++11 中用“auto”推导时 lambda 的类型是啥?【英文标题】:What is the type of lambda when deduced with "auto" in C++11?在 C++11 中用“auto”推导时 lambda 的类型是什么? 【发布时间】:2011-12-18 13:53:51 【问题描述】:

我有一种感觉,lambda 的类型是一个函数指针。当我执行以下测试时,我发现它是错误的(demo)。

#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 !

上面的代码是否缺少任何一点?如果不是那么,当用auto关键字推导出来的typeof一个lambda表达式是什么?

【问题讨论】:

“lambda 的类型是一个函数指针”——这将是低效的并且错过了 lambdas 的全部要点。 【参考方案1】:

这可能有效:

    h1 
        font-size:20px;
      
    h2
        font-size:18px;
      
    p 
        font-size: 16px;
      
    exmp
        font-size:16px;
        color:#000077;
        /*font-style: oblique;*/
        font-family: Lucida Console;
      
<h1>If you truly insist in defining a datatype other then auto for your lambda variable then I would recommend the following</h1>

    <h2>Step 1: </h2>
    <p>Typedef a function pointer</p>
    <exmp> typedef void(*FuncPointerType)();</exmp>
    <p>Note the empty parentheses, this will need to be the same as the arguments later of your lambda <br> Now create a function pointer as you would normaly do.</p>
<exmp>/void (**MyFunction)() = new FuncPointerType([]());</exmp>
<p>Note that the you will have to go and manually delete the pointer as it is created on the heap<br>Finally call the function pointer, it can be called one of 2 ways:</p>
<exmp>(*(*MyFunction))();</exmp>
<p>OR</p>
<exmp>(*MyFunction)();</exmp>
<p>Note the importance that it should be returnd for a function pointer pointer to just a function pointer.</p>

【讨论】:

【参考方案2】:

还应注意 lambda 可转换为函数指针。然而 typeid 返回一个非平凡的对象,该对象应该不同于 lambda 和泛型函数指针。所以对 typeid 的测试不是一个有效的假设。一般来说,C++11 不希望我们担心类型规范,如果给定类型可以转换为目标类型,这一切都很重要。

【讨论】:

这很公平,但是打印类型对于获得正确的类型还有很长的路要走,更不用说捕捉类型可转换但不满足其他约束的情况。 (我总是尽可能地推动“具体化”约束,但尝试这样做的人更有理由在开发过程中展示他们的工作。)【参考方案3】:

[C++11: 5.1.2/3]: lambda-expression的类型(也是闭包对象的类型)是唯一的、未命名的非联合类type — 称为 闭包类型 — 其属性如下所述。此类类型不是聚合 (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-capturelambda-expression 的闭包类型有一个公共的非虚拟非显式 const 转换函数到指向函数的指针与闭包类型的函数调用运算符相同的参数和返回类型。这个转换函数的返回值应该是一个函数的地址,当被调用时,它与调用闭包类型的函数调用运算符具有相同的效果。

最后一段的结果是,如果您使用转换,您将能够将LAMBDA 分配给pFptr

【讨论】:

【参考方案4】:

来自How can I store a boost::bind object as a class member? 的实用解决方案,试试boost::function&lt;void(int)&gt;std::function&lt;void(int)&gt;

【讨论】:

但是请注意这样做的性能成本(每次调用函数时都会调用虚函数)。【参考方案5】:
#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 引入了新类型(如仿函数)。

【讨论】:

我推荐the CXXABI unmangling approach,如果你已经走这条路了。相反,我通常使用__PRETTY_FUNCTION__,如template&lt;class T&gt; const char* pretty(T &amp;&amp; t) return __PRETTY_FUNCTION__; ,如果它开始变得拥挤,就去掉多余的。我更喜欢看到模板替换中显示的步骤。如果您缺少__PRETTY_FUNCTION__,则可以使用 MSVC 等替代方法,但由于需要 CXXABI 的相同原因,结果始终取决于编译器。【参考方案6】:

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

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

【讨论】:

不错的答案。比我的精确多了。 +1 :) 唯一性部分+1,一开始非常令人惊讶,值得关注。 这并不重要,但类型真的没有命名,还是只是在编译时才命名? IOW,可以使用 RTTI 找到编译器决定的名称吗? @Ben,它没有命名,就 C++ 语言而言,没有“编译器决定的名称”之类的东西。 type_info::name() 的结果是实现定义的,所以它可能返回任何东西。实际上,编译器会为链接器命名类型。 最近,当被问到这个问题时,我通常会说lambda的类型一个名字,编译器知道它,它只是说不出来。【参考方案7】:

未指定 lambda 表达式的类型。

但它们通常只是函子的语法糖。一个 lambda 被直接翻译成一个仿函数。 []里面的任何东西都变成了构造函数参数和functor对象的成员,()里面的参数变成了functor的operator()的参数。

不捕获任何变量的 lambda([] 内没有任何内容)可以转换为函数指针(如果那是您的编译器,MSVC2010 不支持此功能,但此转换是标准的一部分)。

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

【讨论】:

MSVC2010 不支持转换为函数指针,但 MSVC11 支持。 blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx +1 表示“函子的语法糖”。记住这一点可以避免很多潜在的混乱。 函子是operator() 基本上是***.com/questions/356950/c-functors-and-their-uses

以上是关于在 C++11 中用“auto”推导时 lambda 的类型是啥?的主要内容,如果未能解决你的问题,请参考以下文章

C++11 学习笔记-类型推导

C++11新特性学习:auto类型推导

[C++11]自动类型推导auto

C++11新特性之autodecltype

C++11新特性之autodecltype

C++11之auto类型推导