I have this piece of C++ code:
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;
}
This does not compile because the construction of objects c1
and c2
seem to involve the class's move constructor:
error: use of deleted function ‘MyClass::MyClass(MyClass&&)’
It seems as if the compiler wants to create temporary object and then move them to c1
and c2
. Why is this happening? Shouldn't all three statements just call the MyClass(Args& a)
constructor?
On the other hand, if I do create the move constructor the program compiles fine and the move constructor is never called!!!
See 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.
Since C++17:
They need not be present or accessible, as the language rules ensure that no copy/move operation takes place, even conceptually.
One major problem is this:
MyClass c1 = MyClass(a);
This creates a temporary object of type MyClass
, and temporary types are rvalues. It then tries to either copy-construct c1
using the temporary object, or if you have a move-constructor then move-construct c1
.
In C++11 and C++14, there will be no auto-generated copy-constructor for your class (because you have a user-defined (even if deleted) move-constructor), so only the deleted move-constructor is available. Well it would be available if it wasn't deleted, leading to your error.
Why is this happening? Shouldn't all three statements just call the
MyClass(Args& a)
constructor?
For both MyClass c1 = MyClass(a);
and MyClass c2 = a;
, temporary MyClass
will be constructed at first by the constructor MyClass::MyClass(Args& a)
, then used to copy-initialize the object c1
and c2
. The constructed temporaries are rvalues, that means the move-constructor will be selected for the copy initialization.
On the other hand, if I do create the move constructor the program compiles fine and the move constructor is never called!!!
The reason is copy elision; the copy/move operation is omitted here, results in the fact that MyClass::MyClass(Args& a)
is used to initialize the object c1
and c2
directly.
The rule about copy elision changed since C++17. Note that pre-C++17, copy elision is not guaranteed. And for non-guaranteed copy elision, even when the copy/move operation is omitted the copy/move constructor still needs to be present and accessible.
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.
After C++17 your code would work fine. For guaranteed copy elision, copy/move constructor is not required to be present or accessible.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With