什么时候'这个'在lambda中捕获?

voi*_*ter 24 c++ lambda c++11

我在一个定义lambda的类中有一个函数,并将其存储在本地静态变量中:

class A
{
public:
    void call_print()
    {
        static auto const print_func = [this] {
            print();
        };

        print_func();
    };

    virtual void print()
    {
        std::cout << "A::print()\n";
    }
};

class B : public A
{
public:
    virtual void print() override
    {
        std::cout << "B::print()\n";
    }
};
Run Code Online (Sandbox Code Playgroud)

我还执行以下测试:

int main()
{
    A a;
    B b;

    a.call_print();
    b.call_print();
}
Run Code Online (Sandbox Code Playgroud)

(现场样本)

我希望打印的是:

A::print()
B::print()
Run Code Online (Sandbox Code Playgroud)

但我真正得到的是:

A::print()
A::print()
Run Code Online (Sandbox Code Playgroud)

(每个都打印相同的对象地址)

我怀疑这是由于this捕获.我假设它会捕获this它被调用的值,但是它似乎是在定义lambda的那一刻捕获的.

有人可以解释lambda捕获的语义吗?他们什么时候实际上被提供给该功能?对于所有捕获类型都是一样的,还是this特殊情况?删除static修复了问题,但是在我的生产代码中,我实际上将lambda存储在一个稍重的对象中,该对象表示我稍后插入信号的插槽.

Nic*_*las 36

这与lambda捕获的语义无关.它只是如何static工作.

一个static函数范围的变量初始化只有一次.整个程序中只有一个这样的对象.它将在第一次调用函数时初始化(更具体地说,第一次static执行语句时).因此,用于初始化static变量的表达式只能被调用一次.

因此,如果使用static基于函数的某个参数(如this)的数据初始化函数范围的变量,那么它将仅从该函数的第一次调用中获取参数.

您的代码创建了一个lambda.它不会在每次调用函数时创建不同的lambdas.

您似乎想要的行为不是函数局部static变量,而是对象成员.所以只需将一个std::function对象放在类本身中,call_print如果它是空的则初始化它.

  • 可能值得补充的是,这实际上代表了一种非常危险的编程风格,因为如果`a`被删除,将来对`call_print`的调用将调用未定义的行为并且很可能会崩溃. (11认同)
  • @ void.pointer:lambda在生成之后的某个时刻如何捕获*任何*?兰巴达不是魔术; 它们只是一种宣称类型的奇特方式. (2认同)

jua*_*nza 12

lambda在第一次A::call_print()调用封闭函数时被实例化.从第一次在A对象上调用它时,this就会捕获该对象的对象.如果你颠倒了调用的顺序,你会看到不同的结果:

b.call_print();
a.call_print();
Run Code Online (Sandbox Code Playgroud)

输出:

B::print()
B::print()
Run Code Online (Sandbox Code Playgroud)

它更多地与函数本地静态对象的初始化语义相比,而不是lambda捕获的语义.


use*_*751 8

静态局部变量在其声明第一次执行时初始化.

在这种情况下,您:

  1. 创建一个lambda,捕获&A,这样调用A.print();
  2. 将lambda分配给 print_func
  3. 称那个lambda(通过print_func)
  4. 再次打电话给那个lambda.

创建lambda时始终捕获捕获的值 - 在这种情况下是在第一次调用时call_print.