As a C++ newbie I really have problems understanding the new Move-Constructor of C++11 and I hope someone can explain a specific situation I stumbled upon. Let's take this example code:
#include <iostream>
using namespace std;
class Model {
public:
int data;
Model(int data) : data(data) { cout << "Constructor" << endl; }
Model(Model&& model) { cout << "Move constructor" << endl; }
~Model() { cout << "Destructor" << endl; }
private:
Model(const Model& model);
const Model& operator=(const Model&);
};
Model createModel(int data) {
return Model(data);
}
int main(void) {
Model model = createModel(1);
cout << model.data << endl;
return 0;
}
So I have created a createModel
function which should return a model as a temporary rvalue and I want to assign it to an lvalue. I don't want the compiler to create copies of the Model
object so I define the copy constructor as private and I do the same with the assignment operator to make sure no data is copied. After doing this the code correctly no longer compiles so I added the Move constructor and now it compiles again. But when I run the program I get this output:
Constructor
1
Destructor
So the Move Constructor was never called. I don't understand why I have to specify the move constructor to be able to compile the program when it is not used at all during runtime.
Is it because the compiler (GCC 4.8.2) optimizes the Move Constructor away? Or is there some other magic performed here?
So can someone please explain what exactly happens in the code above? The code does what I want but I really don't understand why.
If any constructor is being called, it means a new object is being created in memory. So, the only difference between a copy constructor and a move constructor is whether the source object that is passed to the constructor will have its member fields copied or moved into the new object.
A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying.
A move constructor allows the resources owned by an rvalue object to be moved into an lvalue without creating its copy. An rvalue is an expression that does not have any memory address, and an lvalue is an expression with a memory address.
There are two moves that could happen in your program:
model
.Both of these moves can be elided by the compiler for the same reason:
when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
There are other situations in which copy/move elision occurs (see §12.8/31 in C++11). Note that copy/move elision is entirely optional - the compiler doesn't have to do it.
Note that the compiler is allowed to optimize absolutely anything away as long as it doesn't change the behaviour of your program (under the as-if rule). The reason that copy/move elision is explicitly mentioned in the standard is because it might change the behaviour of your program if your copy/move constructors have side effects. The compiler is allowed to perform this optimization even if it changes the behaviour of your program. This is why your copy/move constructors should never have side effects, because then your program will have multiple valid execution paths.
You can pass the -fno-elide-constructors
option to gcc
to ensure that this optimization is never performed.
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