为什么这个C++ 11 lambda不像我期望的那样?

Joh*_*uld 8 c++ lambda closures c++11

我遇到了一个挑战我对C++ lambdas的新生理解的情况,我把它简化为以下内容:

#include <iostream>

void test()
{
    int (*func)();

    func =
        []()->int {
            std::cerr << "func()" << std::endl;
            return 0;
        };

    int i = 0;
    func =
        [i]()->int {
            std::cerr << "func(): i= " << i << std::endl;
            return 0;
        };
}
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,我将一个非常简单的lambda分配给一个函数指针,它看起来像我期望的那样工作.我在第二种情况下尝试做的是为lambda提供对值的访问i.我对[i]()->int {代码的理解}是它定义了一个没有参数的无名函数,返回一个int并且,通过C++ 11独角兽的魔力,知道它的当前值i.我希望这个lambda应该可以调用int(*)().

test.cpp: In function ‘void test()’:
test.cpp:14:7: error: cannot convert ‘test()::__lambda1’ to ‘int (*)()’ in assignment
  func =
       ^
Run Code Online (Sandbox Code Playgroud)

似乎gcc 4.8.1和4.8.2不同意我的评估(4.4.1甚至拒绝讨论此事).

这似乎表明第二个lambda的类型与函数指针不是赋值兼容的.我不明白为什么会出现这种情况,因为该表达式应该可以调用为" int(*)()".

我的理解(或独角兽)在哪里失败了?

qua*_*dev 9

C++标准,第5.1.2/6节,定义了lambda如何转换为(可能是模板)函数指针.

特别是:

对于一个非通用λ-表达的闭合类型没有λ-捕获具有公共非虚拟非显式const的转换功能,以函数指针与C++语言联动装置(7.5),其具有相同的参数和返回类型为闭包类型的函数调用操作符.此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果

由于lambda具有捕获功能,因此无法转换为函数指针.


注意:

  • 请注意,捕获可以是隐式的,也可以是lamdba的默认值.

即以下内容无效:

int i = 0;
func =
    [&]()->int {
        std::cout << "func(): i= " << i << std::endl;
        return 0;
    }
Run Code Online (Sandbox Code Playgroud)

现场演示


Yak*_*ont 2

可通过签名调用int()并不意味着它可以转换为int(*)(). 任何具有重载的东西都int operator()()可以通过这种方式调用。

Lambda 函数通过这样的重载创建了漂亮的标准类,然后将它们包装在一些语法糖中。(不真实,但足够真实)1

无状态 lambda(不捕获任何内容)会带来额外的operator int(*)()超载(或operator Signature*一般情况),但这只是额外的好处,而不是 lambda 的核心。

实际上,函数指针是调用函数时执行跳转到的地址。也没有空间容纳数据指针(用于存储捕获的变量的状态)。理论上,您可以在执行代码旁边或内部为数据分配空间,但许多系统出于安全原因也会阻止将可写页面标记为可执行,这使得该系统不切实际。有人提出过这样的延期。


1与 C++ 标准中的许多内容一样,内容是根据行为而不是实现来指定的。lambda 的大多数功能都可以通过为您实现“自动编写”的 bog 标准类来复制,但即便如此,编译器也不必这样做。某些 lambda 实现(例如基于堆栈帧捕获的[&]lambda)无法在 C++ 语言中实现。