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

时间:2016-07-13 14:45:23

标签: c++ c++11 lambda

我在一个定义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();
}

Live Sample

我期望印刷的是:

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

但我真正得到的是:

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

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

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

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

6 个答案:

答案 0 :(得分:36)

这与lambda捕获的语义无关。这只是static的工作原理。

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

因此,如果使用基于函数参数之一的数据(如static)初始化this函数作用域变量,那么它只会得到第一次调用该函数时的参数。

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

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

答案 1 :(得分:12)

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

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

输出:

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

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

答案 2 :(得分:8)

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

在这种情况下,你:

  1. 创建一个lambda,捕获&amp; A,调用A.print();
  2. 将该lambda分配给print_func
  3. 调用lambda(通过print_func
  4. 再次调用lambda。
  5. 创建lambda时始终捕获捕获的值 - 在这种情况下是在第一次调用call_print期间捕获的值。

答案 3 :(得分:4)

我不认为捕获是问题,而是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,然后再使用该对象。

答案 4 :(得分:3)

这个问题不是关于lambdas的行为,而是关于函数中静态变量的行为。

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

示例:

#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;

答案 5 :(得分:2)

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