std::move 已使用,已调用移动构造函数但对象仍然有效
std::move used, move constructor called but object still valid
有人可以解释为什么通过 std::move 传递给新对象的原始对象在之后仍然有效吗?
#include <iostream>
class Class
{
public:
explicit Class(const double& tt) : m_type(tt)
{
std::cout << "defaultish" << std::endl;
};
explicit Class(const Class& val) :
m_type(val.m_type)
{
std::cout << "copy" << std::endl;
};
explicit Class(Class&& val) :
m_type(val.m_type)
{
m_type = val.m_type;
std::cout << "move: " << m_type << std::endl;
};
void print()
{
std::cout << "print: " << m_type << std::endl;
}
void set(const double& tt)
{
m_type = tt;
}
private:
double m_type;
};
int main ()
{
Class cc(3.2);
Class c2(std::move(cc));
c2.print();
cc.set(4.0);
cc.print();
return 0;
}
输出如下:
defaultish
move: 3.2
print: 3.2
print: 4
我预计对 cc.set() 和 cc.print() 的调用会失败...
更新
感谢下面的答案,我们已经确定 1) 我没有在移动构造函数中移动任何东西,并且 2) std::move()
在 int 或 double 上没有做任何事情,因为移动这些类型比移动这些类型更昂贵简单地复制。下面的新代码将 class 的私有成员变量更新为 std::string 类型而不是双精度类型,并在 [=37= 中设置此私有成员变量时正确调用 std::move ]' 移动构造函数,导致输出显示 std::move 如何导致 有效但未指定的状态
#include <iostream>
#include <string>
class Class
{
public:
explicit Class(const std::string& tt) : m_type(tt)
{
std::cout << "defaultish" << std::endl;
};
explicit Class(const Class& val) :
m_type(val.m_type)
{
std::cout << "copy" << std::endl;
};
explicit Class(Class&& val) : m_type(std::move(val.m_type))
{
std::cout << "move: " << m_type << std::endl;
};
void print()
{
std::cout << "print: " << m_type << std::endl;
}
void set(const std::string val )
{
m_type = val;
}
private:
std::string m_type;
};
int main ()
{
Class cc("3.2");
Class c2(std::move(cc));
c2.print( );
cc.print();
cc.set( "4.0" );
cc.print();
return 0;
}
最后输出:
defaultish
move: 3.2
print: 3.2
print:
print: 4.0
因为标准是这么说的。
移出的对象具有有效但未指定的状态。这意味着你仍然可以使用它们,但你不能确定它们将处于什么状态。它们可能看起来像移动前一样,这取决于什么是最有效的 "move" 数据输出方式他们中的。例如,int
中的 "moving" 没有任何意义(您必须做 额外的 工作才能重置原始值!)所以 "move" 来自 int
实际上只会是一个副本。 double
.
也是如此
尽管在这种情况下,更多的是因为您实际上没有移动任何东西。
在代码示例中,std::move
确定调用哪个构造函数。而已。所以 c2(std::move(cc))
调用 Class
的移动构造函数。 Class
的移动构造函数不对其参数做任何事情,因此 cc
没有改变,它可以像调用移动构造函数之前那样使用。
评论和答案中关于已移动对象状态的所有讨论都是关于 标准库 类型的要求,这些类型将留在 "valid but unspecified state"(17.6.5.15,[lib.types.movedfrom])。您对类型所做的操作不受此影响。
编辑:叹息。您编辑了代码并更改了问题。现在您的 class 持有 std::string
而不是 float
情况有所不同,cc
中的 std::string
对象确实在 "valid but unspecified state".
有人可以解释为什么通过 std::move 传递给新对象的原始对象在之后仍然有效吗?
#include <iostream>
class Class
{
public:
explicit Class(const double& tt) : m_type(tt)
{
std::cout << "defaultish" << std::endl;
};
explicit Class(const Class& val) :
m_type(val.m_type)
{
std::cout << "copy" << std::endl;
};
explicit Class(Class&& val) :
m_type(val.m_type)
{
m_type = val.m_type;
std::cout << "move: " << m_type << std::endl;
};
void print()
{
std::cout << "print: " << m_type << std::endl;
}
void set(const double& tt)
{
m_type = tt;
}
private:
double m_type;
};
int main ()
{
Class cc(3.2);
Class c2(std::move(cc));
c2.print();
cc.set(4.0);
cc.print();
return 0;
}
输出如下:
defaultish
move: 3.2
print: 3.2
print: 4
我预计对 cc.set() 和 cc.print() 的调用会失败...
更新
感谢下面的答案,我们已经确定 1) 我没有在移动构造函数中移动任何东西,并且 2) std::move()
在 int 或 double 上没有做任何事情,因为移动这些类型比移动这些类型更昂贵简单地复制。下面的新代码将 class 的私有成员变量更新为 std::string 类型而不是双精度类型,并在 [=37= 中设置此私有成员变量时正确调用 std::move ]' 移动构造函数,导致输出显示 std::move 如何导致 有效但未指定的状态
#include <iostream>
#include <string>
class Class
{
public:
explicit Class(const std::string& tt) : m_type(tt)
{
std::cout << "defaultish" << std::endl;
};
explicit Class(const Class& val) :
m_type(val.m_type)
{
std::cout << "copy" << std::endl;
};
explicit Class(Class&& val) : m_type(std::move(val.m_type))
{
std::cout << "move: " << m_type << std::endl;
};
void print()
{
std::cout << "print: " << m_type << std::endl;
}
void set(const std::string val )
{
m_type = val;
}
private:
std::string m_type;
};
int main ()
{
Class cc("3.2");
Class c2(std::move(cc));
c2.print( );
cc.print();
cc.set( "4.0" );
cc.print();
return 0;
}
最后输出:
defaultish
move: 3.2
print: 3.2
print:
print: 4.0
因为标准是这么说的。
移出的对象具有有效但未指定的状态。这意味着你仍然可以使用它们,但你不能确定它们将处于什么状态。它们可能看起来像移动前一样,这取决于什么是最有效的 "move" 数据输出方式他们中的。例如,int
中的 "moving" 没有任何意义(您必须做 额外的 工作才能重置原始值!)所以 "move" 来自 int
实际上只会是一个副本。 double
.
尽管在这种情况下,更多的是因为您实际上没有移动任何东西。
在代码示例中,std::move
确定调用哪个构造函数。而已。所以 c2(std::move(cc))
调用 Class
的移动构造函数。 Class
的移动构造函数不对其参数做任何事情,因此 cc
没有改变,它可以像调用移动构造函数之前那样使用。
评论和答案中关于已移动对象状态的所有讨论都是关于 标准库 类型的要求,这些类型将留在 "valid but unspecified state"(17.6.5.15,[lib.types.movedfrom])。您对类型所做的操作不受此影响。
编辑:叹息。您编辑了代码并更改了问题。现在您的 class 持有 std::string
而不是 float
情况有所不同,cc
中的 std::string
对象确实在 "valid but unspecified state".