如果我在函数中创建一个lambda并使用std :: move将变量捕获到lambda,那么移动何时发生?是在创建lambda还是执行lambda时?
以下面的代码为例......各种动作何时发生?如果在一个线程上调用myFunction并且在另一个线程上执行testLambda,它是否是线程安全的?
class MyClass {
private:
// Only accessed on thread B
std::vector<int> myStuff;
// Called from thread A with new data
void myFunction(const std::vector<int>&& theirStuff) {
// Stored to be called on thread B
auto testLambda = [this, _theirStuff{ std::move(theirStuff) }]() {
myStuff = std::move(_theirStuff);
};
// ... store lambda
}
// Elsewhere on thread A
void someOtherFunction() {
std::vector<int> newStuff = { 1, 2, .... n };
gGlobalMyClass->myFunction(std::move(newStuff));
}
如果我在函数中创建一个lambda并使用std :: move将变量捕获到lambda,那么移动何时发生?是在创建lambda还是执行lambda时?
如果你写了我认为你打算写的东西,那么答案就是:两者兼而有之。目前,答案是:既不是。你有一个lambda捕获_theirStuff { std::move(theirStuff) }
。这基本上声明了闭包类型的一个成员,它将在创建闭包对象时初始化,就像它一样
auto _theirStuff { std::move(theirStuff) };
你也有
myStuff = std::move(_theirStuff);
在lambda体内。
但是,您的参数theirStuff
实际上是对const std::vector<int>
的右值引用。因此,_theirStuff { std::move(theirStuff) }
实际上不会进行移动,因为const std::vector
无法移动。最有可能的是,你想要写std::vector<int>&& theirStuff
。此外,正如@JVApen在下面的评论中指出的那样,你的lambda不可变。因此,_theirStuff
实际上也将是const,因此也无法移动。因此,尽管所有的std::move
,上面的代码实际上每次都会复制矢量。如果你写过
void myFunction(std::vector<int>&& theirStuff)
{
auto testLambda = [this, _theirStuff { std::move(theirStuff) }]() {
myStuff = std::move(_theirStuff);
};
}
在创建闭包对象时,您将theirStuff
移动到_theirStuff
。当lambda被调用时,你会将_theirStuff
复制到myStuff
。如果你写过
void myFunction(std::vector<int>&& theirStuff)
{
auto testLambda = [this, _theirStuff { std::move(theirStuff) }]() mutable {
myStuff = std::move(_theirStuff);
};
}
然后,当创建闭包对象时,您将把theirStuff
移动到_theirStuff
。当lambda被召唤时,你会将_theirStuff
移动到myStuff
。请注意,因此,您的lambda无法真正被调用两次。我的意思是,它可以,但它只会真正起作用,因为_theirStuff
在第一次调用lambda后将是空的...
另请注意,上述说明仅适用于示例中特定类型的组合。移动对象实际上没有一般定义。移动对象意味着完全取决于对象的特定类型。它甚至可能没有任何意义。 std::move
本身并没有做任何事情。它只是将给定的表达式转换为右值引用。如果然后从std::move
的结果初始化另一个对象,或将结果分配给对象,则重载决策将选择移动构造函数或移动赋值运算符(如果存在) - 而不是正常的复制构造函数或复制赋值运算符。然后由相应类型的移动构造器/移动赋值运算符的实现来实际执行移动,即,在初始化或从右值分配的情况下,做对应于特定类型的任何操作。因此,在某种程度上,当你应用std::move
时你所做的是你将相应的对象宣传为“这可能会被移出”。它是否实际上将被移动(如果是这样,实际意味着什么)取决于实现。在std::vector
的特定情况下,移动构造函数/移动赋值运算符,根据定义,保证不仅原始向量的内容将从原始对象接管,而且原始对象之后将为空。在许多其他情况下,对于从被移动的对象做任何事情可能是未定义的行为(除了,可能,销毁它;那个可以被认为是理所当然的类型,至少不允许这样做几乎没用;通常,您至少能够为移动的对象分配新值,但即使这样也不能保证。您总是必须检查手头的特定类型,在移动之后保证对象的状态是什么...