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

我一直在尝试编写一个无法复制但可以移动的类,除了使用命名构造函数之外,无法创建该类.我用下面的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

问题:

>为什么在namedConstructor2中的移动构造函数之前调用析构函数,如输出的第3行和第4行所示?
>为什么被破坏的数据仍然可用于移动的构造函数(由输出的第4行证明)?
>在std :: move的签名导致我认为std :: move的返回值有“两个&&”这个意义上,nameConctor2不是命名为Constructor2“更自然”吗?

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);到主要功能.新输出(在发布模式下编译,而不是在调试模式下)显示最佳情况甚至不移动对象:

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

最佳答案 std :: move(lvalue或xvalue)不会调用析构函数.

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

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

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

> namedConstructor1返回rvalue-reference.
但是你试图返回一个本地(这是一个左值),并且编译器抱怨该错误.
> namedConstructor2原则上与namedConstructor1相同,但是你从lvalue到rvalue-reference添加了一个显式的强制转换(以std :: move的形式),这会关闭编译器.
因此,在使用返回的rvalue-reference之前,您将获得UB以便向编译器说谎,特别是locals生命周期在函数结束时结束.
> namedConstructor3没问题,虽然不是最佳的.
您正在使用rvalue-reference初始化返回值(不是引用).
次优部分是由于std :: move禁用返回值优化,这将消除实现在这种情况下实际移动(或复制)的需要.

点赞