lambda 是如何移动的?

How is a lambda moved?

我不明白 lambda 是如何移动的。考虑以下代码:

#include <iostream>
#include <utility>
#include <string>

struct Foo // non-copyable type
{
    Foo() = default;
    Foo(const Foo&) = delete; // cannot copy
    Foo(Foo&&) = default; // can move
};

auto lambda = [p = Foo()](){
    std::string x{"lambda"}; 
    std::cout << x << std::endl;
};
// auto copied_lambda = lambda; // cannot copy due to Foo non-copyable init capture
auto moved_lambda = std::move(lambda); // we moved it

struct Functor // "simulate" a lambda
{
    std::string x{"functor"};
    void operator()() const
    {
        std::cout << x << std::endl;
    }
};

Functor functor; // initial functor object
auto moved_functor = std::move(functor); // moved it

int main()
{
    lambda(); // why does it display "lambda" since it was moved?
    moved_lambda(); // displays "lambda", was moved in, everything OK

    functor(); // doesn't display "functor", was moved
    moved_functor(); // displays "functor", was moved in, everything OK
}

如您所见,我声明了一个 Foo 不可复制 class(但可移动),我将其传递给 lambda 的初始捕获,因此 lambda最终只能移动。 lambda 闭包声明了一个 std::string (它是可移动的)。接下来我将 lambda 移动到 moved_lambda,因此我希望 std::string 也被移动。但是,当我在 main() 中调用 lambda() 时,它仍然显示旧字符串,就好像它根本没有移动一样。

然后我 "simulated" 带有仿函数的 lambda,当将 functor 移动到 moved_functor 时,functor 中的初始 string 也被移动,所以当我尝试在 main() 中显示它时,我得到一个空字符串。我对这种行为感到困惑,为什么会出现这种不一致?我原以为移动的 lambda(在内部只是一个函数对象)会移动其组件而不是复制它们。我唯一的解释是 lambda 中的局部变量是 const,所以我们不是移动它们而是复制它们,但我不确定。是这样吗?

您的 Functor 与您的 lambda 不一致。你的 lambda 实际上看起来像:

struct Functor // "simulate" a lambda
{
    void operator()() const
    {
        std::string x{"functor"};
        std::cout << x << std::endl;
    }
};

希望这有助于弄清楚为什么你的移出 lambda 仍然打印 "lambda"Functor 有它的 x 移出,但 lambda 没有。

你举的例子不一样。 lambda 的正确模拟是:

struct Functor // "simulate" a lambda
{
    void operator()() const
    {
        std::string x{"functor"};
        std::cout << x << std::endl;
    }
};

请注意,x 现在位于函数内部。这两个示例现在的行为相同:即使它们被移动了,它们也都打印了它们的局部变量。

解释很简单:内部变量不会被复制或移动,因为它们在 functor/lambda 被复制或移动时不存在。它们在 operator() 被调用时被创建和销毁。