【问题标题】:What type do lambdas get compiled into? [duplicate]lambda 被编译成什么类型​​? [复制]
【发布时间】:2014-11-09 23:41:46
【问题描述】:

据我所知,所有数据类型都必须在编译时知道,而 lambda 不是一种类型。 lambda 是否被翻译成 anonymous struct with operator()std::function 包装?

例如,

std::for_each(v.begin(), v.end(), [](int n&){n++;});

【问题讨论】:

  • std::function 没有像 std::initializer_list 这样的语言连接。
  • 这源于我缺乏知识。纯随机。
  • Lambda 类型是unspecified
  • 我只想指出 lambda 的类型,尽管未指定且不可表达,但 可以 由模板推导出来。我认为那个很强大。

标签: c++ c++11 lambda


【解决方案1】:

as-if rule 的变体,C++11 标准说:

§5.1.2/3 [..] 一个实现可以定义闭包类型 与下面描述的不同,前提是这不会改变 程序的可观察行为,而不是通过更改:

——闭包类型的大小和/或对齐方式,

——闭包类型是否可简单复制(第 9 条),

——闭包类型是否是标准布局类(第 9 条),或者

——闭包类型是否为 POD 类(第 9 条)。

我相信这就是人们所说的unspecified 的意思。但是,如其他答案中所述,保证如下:

原作者:Lightness Races in Orbit

[C++11: 5.1.2/3]: lambda-expression的类型(也是闭包对象的类型)是唯一的,未命名的 非联合类类型——称为闭包类型——其属性 如下所述。此类类型不是聚合 (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 将函数转换为指向具有相同参数的函数的指针 并返回类型作为闭包类型的函数调用运算符。这 此转换函数返回的值应为 该函数在调用时与调用 闭包类型的函数调用运算符。

【讨论】:

    【解决方案2】:

    来自标准§5.1.2.3:

    lambda 表达式的类型...是唯一的、未命名的非联合类类型

    它是它自己的类型。每次。比如:

    auto a = []{ return 1; };
    auto b = []{ return 1; };
    

    ab 必然有不同的类型。它们都可以转换为std::function<int()>,但不能相互转换:

    std::function<int()> c = a; // OK
    a = b; // NOPE
    

    添加更多示例以增加清晰度:

    decltype(a) a2 = a; // OK, explicitly specifying the correct type
    
    template <typename F>
    void foo(F f) { ... }
    
    foo(a); // calls foo<decltype(a)>, not foo<std::function<int()>
    

    【讨论】:

    • 这不能回答我的问题。我知道 lambda 没有类型(正如我在问题中所述)。我的问题是它们是如何编译的,因为它们没有类型。
    • @texasbruce 你误会了。他们确实有一个类型。它只是独特的未命名的。在我的示例中,您可以执行 using T = decltype(a); T a2 = a; 编译即可。
    • @texasbruce:答案说 lambda 表达式的类型是唯一的、未命名的非联合类类型。这与“没有类型”不同。 lambda 确实 有一个类型,但该类型没有名称。
    • @Barry,我相信using T = decltype(a) 实际上是非法的。 stackoverflow.com/questions/4846540/c11-lambda-in-decltype
    • @sfjac 他在表达式而不是结果对象上使用decltype。一旦我们有了a,它就像任何其他对象一样只是一个对象。示例:ideone.com/V7lXdg
    【解决方案3】:

    一个 lambda 表达式构造一个未命名的类型,每个类型都有不同的类型。它们不是std::function 实现。此处提供了更多信息: What is a lambda expression in C++11? 和这里:How to convert a lambda to an std::function using templates

    您可以使用以下技巧在特定编译器上揭示类型:

    void foo(int);
    
    int main() {
        auto a = []{ return 1; };
        auto b = []{ return 1; };
    
        foo(a);
    
        foo(b);
    
        return 0;
    }
    

    在我的 mac 上使用 clang 编译会得到:

    /Users/jcrotinger/ClionProjects/so_lambda_type/main.cpp:11:5: error: no matching function for call to 'foo'
        foo(a);
        ^~~
    /Users/jcrotinger/ClionProjects/so_lambda_type/main.cpp:5:6: note: candidate function not viable: no known conversion from 
    '<lambda at /Users/jcrotinger/ClionProjects/so_lambda_type/main.cpp:8:14>' to 'int' for 1st argument
    void foo(int);
         ^
    /Users/jcrotinger/ClionProjects/so_lambda_type/main.cpp:13:5: error: no matching function for call to 'foo'
        foo(b);
        ^~~
    /Users/jcrotinger/ClionProjects/so_lambda_type/main.cpp:5:6: note: candidate function not viable: no known conversion from 
    '<lambda at /Users/jcrotinger/ClionProjects/so_lambda_type/main.cpp:9:14>' to 'int' for 1st argument
    void foo(int);
    

    @Barry 指出您可以改用typeid。如果我在我的系统上打印出typeid(a).name()typeid(b).name(),我会得到:

    Z4mainE3$_0
    Z4mainE3$_1
    

    哪一个 demangle 到

    main::$_0
    main::$_1
    

    为了完整起见,只是想包含这个。实际上,我发现错误消息版本信息量更大。 :)

    【讨论】:

    • 所以它是匿名结构?
    • 根据@texasbruce 的阐述,它们有一个类型,但它是一个实现细节。
    • @sfjac 如果你真的想看类型,你可以使用typeid() 并通过demangler 传递它
    • @Barry 当 gcc 工具链已经提供了一个 demangler 时,它不太可能是必需的。 c++filt
    • @remyabel 当然,但也许您想编写一个在代码运行时打印类型的函数? c++filt 非常适合检查符号文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-11
    • 2013-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-08
    • 2014-09-23
    相关资源
    最近更新 更多