std :: move意外地调用了析构函数

时间:2014-12-04 01:16:56

标签: c++ c++11 move-semantics

我一直在尝试编写一个无法复制但可以移动的类,除了使用命名构造函数之外,无法创建该类。我通过下面的namedConstructor3实现了我的目标。但是,我不明白为什么namedConstructor2失败了。

struct  A
{
    int a;

    //static A && namedConstructor1( int a_A )
    //{
    //  A d_A( a_A );
    //  return d_A;     // cannot convert from A to A&&
    //}

    static A && namedConstructor2( int a_A )
    {
        wcout << L"Named constructor 2\n";
        A d_A( a_A );
        return move( d_A );
    }

    static A namedConstructor3( int a_A )
    {
        wcout << L"Named constructor 3\n";
        A d_A( a_A );
        return move( d_A );
    }

    A( A && a_RHS ) : a( a_RHS.a )
    {
        a_RHS.a = 0;
        wcout << L"\tMoved: a = " << a << endl;
    }

    ~A()
    {
        wcout << L"\tObliterated: a = " << a << endl;
        a = -a;
    }

    A( const A & ) = delete;
    A & operator =( const A & ) = delete;

protected:
    A( int a_A = 0 ) : a( a_A )
    {
        wcout << L"\tCreated: a = " << a << endl;
    }
};

int main()
{
    A d_A2 = A::namedConstructor2( 2 );
    A d_A3 = A::namedConstructor3( 3 );
    wcout << "Going out of scope\n";
    return 0;
}

输出

Named constructor 2
        Created: a = 2
        Obliterated: a = 2
        Moved: a = -2
Named constructor 3
        Created: a = 3
        Moved: a = 3
        Obliterated: a = 0
Going out of scope
        Obliterated: a = 3
        Obliterated: a = -2

问题:

  1. 为什么在namedConstructor2中的移动构造函数之前调用析构函数,如输出的第3行和第4行所示?

  2. 为什么被破坏的数据仍可供移动的构造函数使用(由输出的第4行证明)?

  3. namedConstructor2的签名导致我认为namedConstructor3的返回值的意义上,std::move“不比std::move更”自然“有“两个&amp;&amp;”?

  4. template< class T >
    typename std::remove_reference<T>::type&& move( T&& t )
    

    使用VS2013u4编译。


    修改

    Deduplicator的回答让我满意。此编辑是为了完整性。答案和评论表明namedConstructor3是“次优的”。我添加了

    static A namedConstructor4( int a_A )
    {
        wcout << L"Named constructor 4\n";
        A d_A( a_A );
        return d_A;
    }
    

    到班级,A d_A4 = A::namedConstructor4( 4 );main。新输出(在发布模式下编译,而不是在调试模式下)显示最佳情况甚至不移动对象:

    Named constructor 2
            Created: a = 2
            Obliterated: a = 2
            Moved: a = -2
    Named constructor 3
            Created: a = 3
            Moved: a = 3
            Obliterated: a = 0
    Named constructor 4
            Created: a = 4
    Going out of scope
            Obliterated: a = 4
            Obliterated: a = 3
            Obliterated: a = -2
    

1 个答案:

答案 0 :(得分:2)

std::move(lvalue or xvalue)不会调用析构函数。

它只是将传递的引用更改为rvalue-reference,因此适用move-semantics。

那么,为什么你的当地太早被摧毁? 简单,返回对局部变量的引用是UB:
Can a local variable's memory be accessed outside its scope?

逐个遍历命名的构造函数:

  1. namedConstructor1返回右值参考 但是你试图返回一个本地(这是一个左值),编译器会抱怨这个错误。
  2. namedConstructor2原则上与namedConstructor1相同,但是您从lvalue到rvalue-reference添加了一个显式强制转换(以std::move的形式),这会关闭编译器。
    因此,在使用返回的rvalue-reference之前,你会得到UB来代替编译器,特别是locals生命周期在函数末尾结束。
  3. namedConstructor3没问题,虽然不是最佳的 您正在使用rvalue-reference初始化返回值(不是引用)。
    次优部分是由于std::move禁用了返回值优化,这样可以避免实现在这种情况下实际移动(或复制)的需要。