std::使用了move,调用了move构造函数,但对象仍然有效



有人能解释为什么通过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)我没有在move构造函数中移动任何东西,2)int或double上的std::move()没有做任何事情,因为移动这些类型比简单地复制更昂贵。下面的新代码将类的私有成员变量更新为std::string类型,而不是double类型,并在class的move构造函数中设置此私有成员变量时正确调用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

因为标准是这么说的。

从对象中移动具有有效但未指定的状态。这意味着你仍然可以使用它们,但你不能确定它们会处于什么状态。它们可能看起来和移动前一样,这取决于从中"移动"数据的最有效方式。例如,从int"移动"是没有意义的(你必须做额外的工作才能重置原始值!),所以从int"移动"实际上只是一个副本。double也是如此。

尽管在这种情况下,更多的是因为你实际上没有移动任何东西。

在代码示例中,std::move决定调用哪个构造函数。没什么了。因此c2(std::move(cc))调用了Class的move构造函数。Class的move构造函数不对其参数执行任何操作,因此cc保持不变,并且可以像调用move构造函数之前一样使用它。

评论和答案中关于已移动对象状态的所有讨论都是关于标准库类型的要求,这些类型将处于"有效但未指定的状态"(17.6.5.15,[lib.types.movedfrom])。您对类型的处理不受此影响。

编辑:叹气。您编辑了代码并更改了问题。现在,您的类持有std::string而不是float,情况有所不同,并且cc中的std::string对象确实处于"有效但未指定的状态"。

最新更新