I posted this code in another question but I have a new doubt on it:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class X
{
public:
std::vector<double> data;
// Constructor1
X():
data(100000) // lots of data
{
cout << "X default constructor called";
}
// Constructor2
X(X const& other): // copy constructor
data(other.data) // duplicate all that data
{
cout << "X copy constructor called";
}
// Constructor3
X(X&& other): // move constructor
data(std::move(other.data)) // move the data: no copies
{
cout << "X move constructor called";
}
X& operator=(X const& other) // copy-assignment
{
cout << "X copy assignment called";
data=other.data; // copy all the data
return *this;
}
X& operator=(X && other) // move-assignment
{
cout << "X move assignment called";
data=std::move(other.data); // move the data: no copies
return *this;
}
};
class X2
{
public:
std::vector<double> data;
// Constructor1
X2():
data(100000) // lots of data
{}
// Constructor2
X2(X const& other): // copy constructor
data(other.data) // duplicate all that data
{}
X2& operator=(X const& other) // copy-assignment
{
data=other.data; // copy all the data
return *this;
}
};
X make_x()
{
X myNewObject; // Il normale costruttore viene chiamato qui
myNewObject.data.push_back(22);
return myNewObject; // Si crea un oggetto temporaneo prima di ritornare con il move constructor perchè myNewObject dev'essere distrutto
}
int main()
{
X x1 = make_x(); // x1 has a move constructor
X2 x2 = make_x(); // x2 hasn't a move constructor
}
In the main() lines I would expect the move assignment and the copy assignment to get called... but they don't!
MSVC2012 output is:
X default constructor called X move constructor called X default constructor called X move constructor called
And g++'s one is
X default constructor called X default constructor called
http://liveworkspace.org/code/220erd$2
Where are the assignments?? I thought the first main() line would call a move assignment and the second main() line would call a copy assignment
// Constructor2
X2(X const& other): // copy constructor
data(other.data) // duplicate all that data
{}
X2& operator=(X const& other) // copy-assignment
{
data=other.data; // copy all the data
return *this;
}
First of all, these are not the copy constructor and copy assignment operator for X2
because they take arguments of type X
. The first is actually known as a converting constructor, because it can convert from an X
to an X2
.
int x = 5;
This is not 5 being assigned to x
; it is x
being initialised with 5
. Initialisation, although it looks similar, is not the same as assignment. In fact, no assignment occurs at all in your code, so the move or copy assignment operators will not be used.
We can take a look at what each of the compilers you've given is actually doing:
MSVC
First, myNewObject
is created in make_x
. This prints out X default constructor called
. Then the return myNewObject;
will treat the copy to the return value as a move first, find that there is a move constructor, and invoke it.
When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.
The return value would then be copied into x1
. However, this copy has clearly been elided because we see no X copy constructor called
output:
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
Secondly, another myNewObject
is created in the second call to make_x
. This again prints out X default constructor called
. Then the same move happens when doing return myNewObject
. The construction of x2
from the return value doesn't output anything because its constructor that takes an X
doesn't do any output.
GCC
First, myNewObject
is created in make_x
, just as with MSVC. This prints out X default constructor called
.
Now GCC does an extra optimization that MSVC isn't doing. It realises that it might as well not bother moving from myNewObject
to the return value and instead just construct it directly in the return value's place:
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cvunqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
Then the same elision caused by constructing x1
from a temporary object is performed as was in MSVC.
The second call to make_x
occurs in exactly the same way as with the first, except now x2
is constructed by the converting constructor that takes an X
. This, of course, doesn't output anything.
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