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

Hec*_*tor 5 c++ move-semantics c++11

我一直在尝试编写一个不能复制但可以移动的类,除非使用命名构造函数,否则不能创建该类。我实现了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;
}
Run Code Online (Sandbox Code Playgroud)

输出是

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
Run Code Online (Sandbox Code Playgroud)

问题:

  1. namedConstructor2如输出的第三行和第四行所证明的,为什么析构函数在move构造函数之前被调用?

  2. 为什么销毁的数据仍可用于移动的构造函数(如输出的第4行所示)?

  3. “不是namedConstructor2更自然”,而不是namedConstructor3从的签名std::move使我认为的返回值std::move具有“两个&&”的意义上说?

????

template< class T >
typename std::remove_reference<T>::type&& move( T&& t )
Run Code Online (Sandbox Code Playgroud)

与VS2013u4一起编译。


编辑

重复数据删除器的答案使我满意。此编辑是出于完整性考虑。答案和评论建议namedConstructor3是“次优”的。我加了

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

在类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
Run Code Online (Sandbox Code Playgroud)

Ded*_*tor 3

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

它只是将传递的引用更改为右值引用,因此移动语义适用。

那么,为什么你的本地被毁得太早了呢?
简单,返回对局部变量的引用就是UB:
局部变量的内存可以在其作用域之外访问吗?

逐一检查您的命名构造函数:

  1. namedConstructor1返回右值引用。
    但是您尝试返回本地值(这是一个左值),编译器会抱怨该错误。
  2. namedConstructor2原则上与 相同namedConstructor1,但您添加了从左值到右值引用的显式转换(以 的形式std::move),这会关闭编译器。
    因此,你会因为对编译器撒谎而得到 UB,特别是在使用返回的右值引用之前,局部生命周期在函数末尾结束。
  3. namedConstructor3还可以,虽然不是最理想的。
    您正在使用右值引用来初始化返回值(不是引用)。
    次优部分是由于std::move禁用返回值优化,这将消除在这种情况下实际移动(或复制)实现的需要。