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

问题描述 投票:0回答:9

我有一种看法,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 !
}

上面的代码有没有遗漏什么?如果不是,那么用

typeof
关键字推导时,
auto
lambda 表达式是什么?

c++ lambda c++11 typeof auto
9个回答
192
投票

lambda 表达式的类型未指定。

但它们通常只是函子的语法糖。 lambda 直接转换为函子。

[]
内的任何内容都将转换为函子对象的构造函数参数和成员,
()
内的参数将转换为函子
operator()
的参数。

不捕获任何变量(

[]
内没有任何内容)的 lambda 可以转换为函数指针(如果您的编译器是这样的话,MSVC2010 不支持此操作,但此转换是标准的一部分)。

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


141
投票

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

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


34
投票

[C++11: 5.1.2/3]:
lambda 表达式 的类型(也是闭包对象的类型) 是一个独特的、未命名的非联合类类型 — 称为 闭包类型 — 其属性已描述以下。该类类型不是聚合 (8.5.1)。闭包类型在包含相应 lambda-expression 的最小块作用域、类作用域或命名空间作用域中声明。 [..]

该子句继续列出这种类型的不同属性。以下是一些亮点:

[C++11: 5.1.2/5]:
lambda-expression 的闭包类型具有公共
inline
函数调用运算符 (13.5.4),其参数和返回类型由 lambda-expressionparameter-declaration-clause 描述分别是尾随返回类型[..]

[C++11: 5.1.2/6]:
没有 lambda-capturelambda-expression 的闭包类型具有公共非虚拟非显式 const 转换函数,用于指向具有与闭包类型的函数调用相同的参数和返回类型的函数操作员。此转换函数返回的值应是函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果。

最后一段的结果是,如果您使用转换,您将能够将

LAMBDA
分配给
pFptr


8
投票
#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 引入了新类型(如函子)。


1
投票

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


0
投票

进一步改进jalf的答案

不捕获任何变量([] 内没有任何内容)的 lambda 可以转换为函数指针

例如:

  typedef int (*Foo)(int a);

  auto bar = [](int a){
    return a;
  };

  int tmp = 1;

  auto bar2 = [tmp](int a){
    return a+tmp;
  };

  Foo foo = bar; // this is ok
  Foo foo2 = bar2; // this gives C/C++(413)

0
投票

这是关于Gabriel回答的详细解释:

只要包含“function”标头,Lambda 函数就可以存储在类型为 function> 的变量中。

#include <iostream>
#include <functional> // <- Required here.
using namespace std;

void put_until_bound(int start, int bound, int dx) {
    function<bool(int)> margin;
        // The value ``margin'' will be determined by the if branches...
    if (dx > 0)             //Then we'll need an upper bound...
      margin = [bound] (int n) {
                 return n >= bound;
               };
    else if (dx < 0)        // Then we'll need a lower bound...
      margin = [bound] (int n) {
                 return n <= bound;
               };
    else                    // By default bound is everywhere...
      margin = [] (int n) {
                 return true;
               };
    for (int x = start; ! margin(x); x += dx)
      cout<<x<<", ";
    cout<<endl;
  }
// ...
// Inside the main function:
    put_until_bound(10, 64, 8);     // => 10, 18, 26, 34, 42, 50, 58, 
    put_until_bound(10, -5, -2);    // => 10, 8, 6, 4, 2, 0, -2, -4, 

您应该使用最新的 g++ 和编译命令,如下所示:

g++ -O3 -o outfile -std=c++2a -fconcepts filename.cxx && ./outfile 

-1
投票

来自 How can I store a boost::bind object as a class member? 的实用解决方案,请尝试

boost::function<void(int)>
std::function<void(int)>


-15
投票

这可能有用:

如果您确实坚持为 lambda 变量定义除 auto 之外的数据类型,那么我会推荐以下

第1步: Typedef 函数指针

typedef void(*FuncPointerType)();

注意空括号,这需要与 lambda 后面的参数相同。现在像平常一样创建一个函数指针。

void (**MyFunction)() = new FuncPointerType([](){});

请注意,您必须手动删除在堆上创建的指针
最后调用函数指针,可以通过以下两种方式之一调用:

(*(*MyFunction))();

(*MyFunction)();

请注意,函数指针指针应该返回到函数指针,这一点很重要。

© www.soinside.com 2019 - 2024. All rights reserved.