为什么这里涉及移动构造函数

Why is the move constructor involved here

我有这段C++代码:

class Args {};

class MyClass {
  public:
  MyClass(Args& a) {}
  MyClass(MyClass &&) = delete;
};

int main() {

  Args a;
  MyClass c1 = MyClass(a);
  MyClass c2 = a;
  MyClass c3(a);

  return 0;
}

这无法编译,因为对象 c1c2 的构造似乎涉及 class 的移动构造函数:

error: use of deleted function ‘MyClass::MyClass(MyClass&&)’

编译器似乎想创建临时对象,然后将它们移动到 c1c2。为什么会这样?这三个语句不应该只调用 MyClass(Args& a) 构造函数吗?

另一方面,如果我确实创建了移动构造函数,程序可以正常编译并且永远不会调用移动构造函数!!!

参见copy elision

Under the following circumstances, the compilers are permitted, but not required to omit the copy- and move- (since C++11) construction of class objects even if the copy/move (since C++11) constructor and the destructor have observable side-effects. This is an optimization: even when it takes place and the copy-/move-constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.

自 C++17 起:

They need not be present or accessible, as the language rules ensure that no copy/move operation takes place, even conceptually.

Why is this happening? Shouldn't all three statements just call the MyClass(Args& a) constructor?

对于MyClass c1 = MyClass(a);MyClass c2 = a;,临时MyClass会先由构造函数MyClass::MyClass(Args& a)构造,然后用于复制初始化对象c1c2。构造的临时对象是右值,这意味着将选择移动构造函数进行复制初始化。

On the other hand, if I do create the move constructor the program compiles fine and the move constructor is never called!!!

原因是copy elision;这里省略了copy/move操作,导致直接使用MyClass::MyClass(Args& a)初始化对象c1c2

关于复制省略的规则自 C++17 以来发生了变化。请注意,在 C++17 之前,不保证复制省略。对于非保证复制省略,即使省略了 copy/move 操作,copy/move 构造函数仍然需要存在和访问。

This is an optimization: even when it takes place and the copy-/move-constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.

在 C++17 之后,您的代码可以正常工作。对于有保证的复制省略,copy/move 构造函数不需要存在或可访问。

Under the following circumstances, the compilers are required to omit the copy- and move- construction of class objects even if the copy/move constructor and the destructor have observable side-effects. They need not be present or accessible, as the language rules ensure that no copy/move operation takes place, even conceptually:

  • In initialization, if the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object:

    T x = T(T(T())); // only one call to default constructor of T, to initialize x
    

一个主要问题是:

MyClass c1 = MyClass(a);

这会创建一个临时类型MyClass对象,临时类型是右值。然后它会尝试使用临时对象 copy-construct c1,或者如果你有一个移动构造函数那么 move-construct c1.

在 C++11 和 C++14 中,不会为您的 class 自动生成复制构造函数(因为您有一个用户定义的(即使已删除)移动构造函数) , 因此只有删除的移动构造函数可用。好吧,如果它没有被删除,它就会可用,导致你的错误。