gpt4 book ai didi

c++ - std::move 意外调用析构函数

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:39:10 24 4
gpt4 key购买 nike

我一直在尝试编写一个不能复制但可以 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

问题:

  1. 为什么析构函数在 namedConstructor2 中的 move 构造函数之前被调用,正如输出的第 3 行和第 4 行所证明的那样?

  2. 为什么被 move 的构造函数仍然可以使用被销毁的数据(输出的第 4 行证明了这一点)?

  3. 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?

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

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

关于c++ - std::move 意外调用析构函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27284651/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com