【问题标题】:Calling std::move without move constructor or move assignment在没有移动构造函数或移动赋值的情况下调用 std::move
【发布时间】:2020-12-30 07:40:09
【问题描述】:

我们知道 std::move does not actually move anything。它只是将左值引用 (&) 转换为右值引用 (&&)。

那么在下面的例子中,拷贝构造函数是如何被调用的呢?如果没有移动构造函数,构造使用 std::move() 的对象如何回退到复制构造函数?变量b 的这种绑定究竟是如何发生的?

struct Test {
  // Default constructor
  Test() {
    std::cout << "Constructor is called." << std::endl;
    mValue = 0;
  }
  
  // Copy constructor
  Test(const Test& rhs) {
    std::cout << "Copy Constructor is called." << std::endl;
    mName = rhs.mName;
    mValue = rhs.mValue;
  }
    
  std::string mName;
  int mValue;
};

int main() {
  Test a;
  Test b = std::move(a);
  return 0;
}

输出:

Constructor is called.
Copy Constructor is called.

【问题讨论】:

  • 因为如果没有更好的候选者,右值引用可以被强制为 const 左值引用,这就是发生的事情。
  • 一个 const 左值引用可以绑定到一个右值。这可以追溯到 C++11 之前的规则。

标签: c++ c++11 move-semantics


【解决方案1】:

让我们通过类比推理。想想这段代码:

void doSomething(const int& x) {
    std::cout << "You like " << x << "? That's my favorite number!" << std::endl;
}

int main() {
    doSomething(137); // <-- Here
}

现在,专注于main 的通话。这段代码编译并运行得很好,但它有点奇怪。注意doSomething 接受const int&amp;。这意味着它需要一个int引用,并且引用(通常)只绑定到左值。但是这里的参数 137 是一个右值。什么给了?

这样做的原因是 C++ 语言特别允许 const 左值引用绑定到右值,即使常规左值引用不能。例如:

const int& totallyLegal = 137; // Yep, that's fine!
int& whoaNotCoolMan     = 42;  // Compile error!

您可以这样做有几个原因。如果您有一个const 左值引用,则您已承诺您可以查看被引用的对象,但不能修改它。因此,将左值引用绑定到右值是安全的,因为这样您就没有办法获取“纯值”并为其分配一些东西。从历史上看,在 C++11 之前,当右值引用不存在时,这使得编写函数成为可能,通过使用 @ 987654329@左值引用。

现在我们有了左值引用,这条规则引入了一些以前没有的混淆点。特别是,const T&amp; 可以绑定到任何T 类型的表达式的结果,即使它是T&amp;T&amp;&amp;。这就是在您的情况下选择复制构造函数的原因。

不过,这里还有一个细微差别。就像 C++ 编译器会自动为类定义默认构造函数、复制构造函数和赋值运算符一样,只要您自己不这样做,C++ 编译器也可以自动定义移动构造函数。但是,有一条规则说,如果一个类型有一个用户定义的复制构造函数,那么编译器不会为你生成一个移动构造函数。因此,您的问题的完整答案是“复制构造函数的存在意味着没有定义移动构造函数,并且由于复制构造函数接受 const 左值引用,它将绑定到右值和左值。”

【讨论】:

  • 如果有移动构造函数,右值将绑定到它。这表明存在一个优先绑定右值的规则:首先尝试将其绑定到移动构造函数(右值引用),如果不成功,将其绑定到复制构造函数(常量左值引用)。这条规则的名称或引用是什么?另外,根据你所说,如果没有复制构造函数或移动构造函数,请将其绑定到自动定义的移动构造函数。
  • 我认为该规则没有特定的名称。这是过载选择如何工作的结果。有一系列规则可以确定重载选择有多“好”,最好将右值引用绑定到右值,而不是将 const 右值引用绑定到右值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-14
  • 2020-03-22
  • 1970-01-01
  • 1970-01-01
  • 2013-01-22
相关资源
最近更新 更多