我一直在尝试编写一个无法复制但可以移动的类,除了使用命名构造函数之外,无法创建该类。我通过下面的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行证明)?
在namedConstructor2
的签名导致我认为namedConstructor3
的返回值的意义上,std::move
“不比std::move
更”自然“有“两个&amp;&amp;”?
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
答案 0 :(得分:2)
std::move(lvalue or xvalue)
不会调用析构函数。
它只是将传递的引用更改为rvalue-reference,因此适用move-semantics。
那么,为什么你的当地太早被摧毁?
简单,返回对局部变量的引用是UB:
Can a local variable's memory be accessed outside its scope?
逐个遍历命名的构造函数:
namedConstructor1
返回右值参考
但是你试图返回一个本地(这是一个左值),编译器会抱怨这个错误。namedConstructor2
原则上与namedConstructor1
相同,但是您从lvalue到rvalue-reference添加了一个显式强制转换(以std::move
的形式),这会关闭编译器。 namedConstructor3
没问题,虽然不是最佳的
您正在使用rvalue-reference初始化返回值(不是引用)。std::move
禁用了返回值优化,这样可以避免实现在这种情况下实际移动(或复制)的需要。