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()
被调用时被创建和销毁。
我不明白 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()
被调用时被创建和销毁。