为什么这里涉及移动构造函数
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;
}
这无法编译,因为对象 c1
和 c2
的构造似乎涉及 class 的移动构造函数:
error: use of deleted function ‘MyClass::MyClass(MyClass&&)’
编译器似乎想创建临时对象,然后将它们移动到 c1
和 c2
。为什么会这样?这三个语句不应该只调用 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)
构造,然后用于复制初始化对象c1
和 c2
。构造的临时对象是右值,这意味着将选择移动构造函数进行复制初始化。
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)
初始化对象c1
和c2
。
关于复制省略的规则自 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 自动生成复制构造函数(因为您有一个用户定义的(即使已删除)移动构造函数) , 因此只有删除的移动构造函数可用。好吧,如果它没有被删除,它就会可用,导致你的错误。
我有这段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;
}
这无法编译,因为对象 c1
和 c2
的构造似乎涉及 class 的移动构造函数:
error: use of deleted function ‘MyClass::MyClass(MyClass&&)’
编译器似乎想创建临时对象,然后将它们移动到 c1
和 c2
。为什么会这样?这三个语句不应该只调用 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)
构造,然后用于复制初始化对象c1
和 c2
。构造的临时对象是右值,这意味着将选择移动构造函数进行复制初始化。
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)
初始化对象c1
和c2
。
关于复制省略的规则自 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 自动生成复制构造函数(因为您有一个用户定义的(即使已删除)移动构造函数) , 因此只有删除的移动构造函数可用。好吧,如果它没有被删除,它就会可用,导致你的错误。