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".