为啥在我的代码中调用复制构造函数而不是移动构造函数?

Posted

技术标签:

【中文标题】为啥在我的代码中调用复制构造函数而不是移动构造函数?【英文标题】:Why copy constructor is called instead of move constructor in my code?为什么在我的代码中调用复制构造函数而不是移动构造函数? 【发布时间】:2019-09-26 03:25:16 【问题描述】:

我试图理解移动构造函数和右值引用。 所以我在https://www.onlinegdb.com/online_c++_compiler 上尝试了这段代码。 但结果让我很困惑。

#include <iostream>
#include <type_traits>


class A 
public:
  A()  std::cout << "Constructed" << std::endl; 
  A(const A& )= delete;
  A(A&&)  std::cout << "Move Constructed" << std::endl; 
;

int
main ()

  A&& a = A();
  A b = a; // error: use of deleted function ‘A::A(const A&)’
  //A b = static_cast<decltype(a)>(a); // This works, WTF?
  std::cout << std::is_rvalue_reference<decltype(a)>::value << std::endl; // pretty sure a is rvalue reference.

  return 0;

【问题讨论】:

decltype(id-expression) 是一个奇怪的野兽,它与其他语言的规则不同,这里它给你A&amp;&amp; 【参考方案1】:

您对 typesvalue categories 感到困惑。

(强调我的)

每个 C++ 表达式(带有操作数、文字、变量名等的运算符)都具有 两个独立的属性:类型值类别

作为命名变量,a 是一个左值。

以下表达式是左值表达式

变量的名称,... ...

然后为A b = a; 选择复制构造函数。正如您所尝试的,static_cast&lt;decltype(a)&gt;(a); 会将其转换为 xvalue(即右值);你也可以使用std::move

A b = std::move(a);

以下表达式是xvalue 表达式

函数调用或重载的运算符表达式,其返回类型为对对象的右值引用,例如std::move(x); ...

【讨论】:

【参考方案2】:
A&& a = A();

不给你一个右值。将任何值视为右值的一种方法是您无法获得它的地址。如果你能得到它的地址,那么它很可能是一个左值。 所以,在你的情况下

auto address_of_a = &a;

是可能的(所以,a 是左值)。

decltype(a)

是 A&& (右值类型)。所以类型转换为 A&& 会给你右值

A b = a;

失败,因为 a 是左值,所以它正在寻找 A(const A&)。

A b = std::move(a); // or
A b = A; // rvalue

会成功,因为现在它会寻找 A(A&&)

A b;
A c = A;
b = std::move(c);

如果您将移动分配定义为已删除,则会失败

b = c;

如果您定义了您的复制分配,将会成功。

只要所有元素都可以简单地复制/移动,默认构造/复制构造/移动或复制分配就可以工作。

【讨论】:

以上是关于为啥在我的代码中调用复制构造函数而不是移动构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥隐式复制构造函数调用基类复制构造函数而定义的复制构造函数不调用?

移动选定的构造函数而不是复制。 [参考标准]

程序使用复制构造函数而不是移动构造函数

为啥这个核心转储错误发生在我的课堂上?我已经包含了复制构造函数、复制赋值和析构函数

为啥我的 Dart 构造函数返回的是动态对象而不是类型对象?

调用复制 ctor 而不是移动 ctor - 编译器可以发出警告吗?