如果我在函数中创建一个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));
}
答案 0 :(得分:1)
如果我在函数中创建一个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
的结果中初始化另一个对象,或将结果分配给对象,则重载解析将选择move构造函数或move赋值运算符(如果存在),而不是普通的复制构造函数或copy Assign运算符。然后取决于相应类型的move构造函数/ move赋值运算符的实现来实际执行移动,即在初始化或从右值进行赋值的情况下,应为特定类型执行应做的任何事情。因此,从某种意义上说,应用std::move
时所要做的是将各个对象广告为“此对象可能从中移出”。它是否会真正从中移出(如果是,这实际上意味着什么)将取决于实现。在std::vector
的特定情况下,根据定义,移动构造函数/移动赋值运算符保证不仅原始矢量的内容将从原始对象中接管,而且原始对象将为空。然后。在许多其他情况下,对从其移出的对象执行任何操作可能是未定义的行为(除了,可以销毁它;可以认为这是理所当然的,因为至少不允许这种类型的对象成为对象)几乎没有用;通常,您至少可以为从中移出的对象分配一个新值,但一般情况下不能保证)。您始终必须检查手头的特定类型,以确保从...移出对象后物体处于何种状态。