什么时候在 lambda 中捕获'this'?



我在一个类中有一个函数,它定义一个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";
    }
};

我还执行以下测试:

int main()
{
    A a;
    B b;
    a.call_print();
    b.call_print();
}

(现场样本)

我期望打印的是:

A::print()
B::print()

但我真正得到的是:

A::print()
A::print()

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

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

有人可以解释 lambda 捕获的语义吗?它们何时实际提供给函数?所有捕获类型都相同,还是this特殊情况?删除static可以解决问题,但是在我的生产代码中,我实际上是将lambda存储在一个稍重的对象中,该对象表示我稍后插入信号的插槽。

这与lambda捕获的语义无关。这就是static的工作方式。

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

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

您的代码创建单个 lambda。它不会在每次调用函数时创建不同的 lambda。

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

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

b.call_print();
a.call_print();

输出:

B::print()
B::print()

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

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

在这种情况下,您:

  1. 创建一个 lambda,捕获 &A,调用 A.print();
  2. 将该 lambda 分配给print_func
  3. 调用该 lambda(通过 print_func
  4. 再次调用该 lambda。

捕获的值始终在创建 lambda 时捕获 - 在本例中是在第一次调用 call_print 期间。

我认为捕获不是这里的问题,而是static关键字。将您的代码视为:

class A
{
public:
    void call_print()
    {
        static A* ptr = this;
        ptr->print();
    };
    virtual void print()
    {
        std::cout << "A::print()n";
    }
};
class B : public A
{
public:
    virtual void print() override
    {
        std::cout << "B::print()n";
    }
};

如果没有 lambda,这几乎是一样的。

如果您查看代码,很明显您的调用a.call_print();使用指向对象的指针初始化ptr a然后进一步使用。

这个问题与其说是关于lambda的行为,不如说是关于函数中静态变量的行为。

变量是在代码第一次流过它时使用当时可用的任何变量创建的。

例:

#include <iostream>
int foo(int v)
{
  static int value = v;
  return v;
};
int main()
{
  std::cout << foo(10) << std::endl;
  std::cout << foo(11) << std::endl;
}

预期:

10
10

因为它相当于:

foo::value = 10;
std::cout << foo::value << std::endl;
// foo::value = 11; (does not happen)
std::cout << foo::value << std::endl;

实际上,捕获的值是在定义 lambda 时设置的,而不是在调用时设置的。因为您要为定义 lambda 的表达式设置静态变量,所以这只会在第一次调用函数 call_print 时发生(通过管理静态变量的规则)。因此,此后的所有call_print调用实际上都会获得相同的 lambda,即其this设置为 &a 的 lambda。

相关内容

最新更新