我一直在尝试编写一个不能复制但可以 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
中的 move 构造函数之前被调用,正如输出的第 3 行和第 4 行所证明的那样?为什么被 move 的构造函数仍然可以使用被销毁的数据(输出的第 4 行证明了这一点)?
在
std::move
的签名引导我的意义上,namedConstructor2
是否比namedConstructor3
“更自然”认为std::move
的返回值有“两个&&”?
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
函数。新输出(在 Release模式下编译时,而不是在 Debug模式下)显示最佳情况甚至不 move 对象:
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 or xvalue)
不调用析构函数。
它只是将传递的引用更改为右值引用,因此 move 语义适用。
那么,为什么你的本地销毁得太早了?
很简单,返回一个局部变量的引用就是UB:
Can a local variable's memory be accessed outside its scope?
逐一检查您的命名构造函数:
namedConstructor1
返回右值引用。
但是您尝试返回一个本地值(它是一个左值),编译器会提示该错误。namedConstructor2
原则上与namedConstructor1
相同,但您添加了一个显式转换(以std::move
的形式)左值到右值引用,这会关闭编译器。
因此,您会因为对编译器说谎而得到 UB,特别是局部生命周期在函数结束时结束,在返回的右值引用被使用之前。namedConstructor3
没问题,但不是最理想的。
您正在使用右值引用来初始化返回值(不是引用)。
次优部分是由于std::move
禁用返回值优化,这将避免在这种情况下实现实际 move (或复制)的需要。
关于c++ - std::move 意外调用析构函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27284651/