【发布时间】:2014-12-04 01:16:55
【问题描述】:
我一直在尝试编写一个无法复制但可以移动的类,并且除了使用命名构造函数之外无法创建。我通过下面的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的返回值有“两个&&”?
template< class T >
typename std::remove_reference<T>::type&& move( T&& t )
用 VS2013u4 编译。
编辑
Deduplicator 的回答让我很满意。此编辑是为了完整起见。答案和 cmets 建议 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
【问题讨论】:
-
您的程序具有未定义的行为,因为构造函数“2”返回对非静态局部变量的引用。
-
@KerrekSB:但是构造函数 3 具有相同类型的变量,不是吗?毕竟,他们的身体是一样的。
-
他们的返回类型不是,这一切都不同。因此
namedConstructor1出错了,而在namedConstructor2中你对编译器撒谎并获得了UB,而在namedConstructor3中你只是对move感到悲观。 -
@Hector 不完全是,返回值不是“本地临时”(至少不是您的函数本地,而是调用者本地)
-
@Hector:返回值不是变量。只是一个值,具体来说就是函数调用表达式的值。
标签: c++ c++11 move-semantics