std::move and construction/destruction of objects - c++

As I remember, before any call of function, it allocs memory for function result and parameters at stack.
Does that means if I have
T func()
{
T a;
return std::move(a);
}
I will still have copying, because it already was allocated memory for entire T?
I also read at similar questions that
return a;
is same as
return std::move(a);
So, I can't avoid copying to stack? Is rvalue a value at stack?
Will it be good way to use it somewhere:
T a = std::move(func());
So I will avoid copying result of function? Do I still have to create special move constructor and move operator=?
I tried to test it and got:
class Temp
{
public:
Temp()
{
cout << "construct" << endl;
i = 5;
}
~Temp()
{
cout << "destruct" << endl;
}
Temp(const Temp& t)
{
i = t.i;
cout << "copy construct" << endl;
}
Temp operator=(const Temp& t)
{
i = t.i;
cout << "operator =" << endl;
return *this;
}
int i;
};
Temp tempfunc1()
{
Temp t1;
t1.i = 7;
return t1;
}
Temp tempfunc2()
{
Temp t1;
t1.i = 8;
return std::move(t1);
}
int main()
{
Temp t1;
Temp t2;
t2.i = 6;
t1 = t2;
cout << t1.i << endl;
t1.i = 5;
t1 = std::move(t2);
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = tempfunc1();
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = std::move(tempfunc1());
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = tempfunc2();
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = std::move(tempfunc2());
cout << t1.i << endl;
}
with result:
construct
construct
operator =
copy construct
destruct
6
operator =
copy construct
destruct
6
NEXT
construct
copy construct
destruct
operator =
copy construct
destruct
destruct
7
NEXT
construct
copy construct
destruct
operator =
copy construct
destruct
destruct
7
NEXT
construct
copy construct
destruct
operator =
copy construct
destruct
destruct
8
NEXT
construct
copy construct
destruct
operator =
copy construct
destruct
destruct
8
As if there is nothing from using std::move. Or it works just if there exist speacial constructor and destructor?
Why "t1 = t2" calls both copy constructor and operator=?
Excuse me for such big heap of questions that may be very simple, even after reading much about that, maybe because my bad english, I still need explanations.
Thank you in advance.

I've made some changes to your code which may help you understand and explore how this all works. I added an id element to each Temp object to make it easier to understand which object is which and also changed the signature of the operator= to return a reference rather than an object. First, here is the required include to make it into a complete program:
#include <iostream>
using std::cout;
using std::endl;
Next, here's the class which now includes a std::move constructor (with the &&) and a move = operator:
class Temp
{
int id;
public:
Temp() : id(++serial), i(5)
{
cout << "construct " << id << endl;
}
~Temp()
{
cout << "destruct " << id << endl;
}
Temp(const Temp& t) : id(++serial), i(t.i)
{
cout << "copy construct " << id << " from " << t.id << endl;
}
Temp(Temp &&t) : id(++serial), i(t.i)
{
t.i = 5; // set source to a default state
cout << "move construct " << id << " from " << t.id << endl;
}
Temp &operator=(const Temp& t)
{
i = t.i;
cout << "operator = " << id << " from " << t.id << endl;
return *this;
}
Temp &operator=(Temp&& t)
{
i = t.i;
t.i = 5; // set source to a default state
cout << "move operator = " << id << " from " << t.id << endl;
return *this;
}
int i;
static int serial;
};
int Temp::serial = 0;
Your functions are still the same, but see the comment
Temp tempfunc1()
{
Temp t1;
t1.i = 7;
return t1;
}
Temp tempfunc2()
{
Temp t1;
t1.i = 8;
return std::move(t1); // not necessary to call std::move here
}
I've slightly altered main() to show how this all works:
int main()
{
Temp t1;
Temp t2;
t2.i = 6;
t1 = t2;
cout << t1.i << endl;
t1.i = 5;
t1 = t2;
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = tempfunc1();
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = std::move(tempfunc1());
cout << t1.i << endl;
cout << "NEXT" << endl;
t1 = tempfunc2();
cout << t1.i << endl;
cout << "NEXT" << endl;
Temp t3(tempfunc1());
cout << t3.i << endl;
cout << "NEXT" << endl;
Temp t4(t1);
cout << t4.i << endl;
}
Finally here is the output:
construct 1
construct 2
operator = 1 from 2
6
operator = 1 from 2
6
NEXT
construct 3
move operator = 1 from 3
destruct 3
7
NEXT
construct 4
move operator = 1 from 4
destruct 4
7
NEXT
construct 5
move construct 6 from 5
destruct 5
move operator = 1 from 6
destruct 6
8
NEXT
construct 7
7
NEXT
copy construct 8 from 1
8
destruct 8
destruct 7
destruct 2
destruct 1
As you can see, with the fixed operator=, no temporary is created. Also once the move version of operator= is provided, temporary objects (as with those returned by your tempfunc1() and tempfunc2() functions) automatically use move semantics. Your tempfunc2() doesn't really need the std::move call within it. As you can see, that merely creates yet another temporary, so it hurts more than it helps. Finally note the in the creation of t3 that there is only a single object created and no temporary or move constructor is required.
update
It's probably worth noting that a move constructor doesn't really help much in this trivial class, but it can help a lot for classes that use allocated memory or are computationally expensive to create. In those cases, it's useful to remember that there are multiple steps required in a move constructor (or in the move version of operator=). Specifically, you must:
release any resources used by the destination object, then
move the resources from the source object to the destination object, then
set the source object state such that the destructor may be called (for example, if the class had memory allocated, you should set the pointer to nullptr so that the destructor will operate correctly) and then finally,
return *this.

Related

C++ constructor takes temporary object not working as expected

For the code below:
struct Test
{
int size;
Test(int a) { cout << "default" << endl; size = a; }
Test(Test const & t) { cout << "copy" << endl; size = t.size + 1; }
Test(Test && t) { cout << "move" << endl; size = t.size + 1; }
~Test() { cout << "destruct" << endl; }
};
struct Test2
{
int size;
Test2(int a) { cout << "default2" << endl; size = a; }
Test2(Test const & t) { cout << "copy2" << endl; size = t.size + 1; }
Test2(Test && t) { cout << "move2" << endl; size = t.size + 1; }
~Test2() { cout << "destruct2" << endl; }
};
I found the result are different as:
Test t{3}; // default
cout << t.size << endl; // 3
Test move_from_temp = Test(Test(std::move(t))); // move
cout << move_from_temp.size << endl; // 4
Test t{3}; // default
cout << t.size << endl; // 3
Test2 move_from_temp = Test2(Test(std::move(t))); // move, move2, destruct
cout << move_from_temp.size << endl; // 5
However, I expect these two should have the same results. Anyone know the reason for this?
If I run your code the result is slightly different (maybe a typo in your Q?)
Test t{3}; // default
cout << t.size << endl; // 3
Test move_from_temp = Test(Test(std::move(t))); // move
cout << move_from_temp.size << endl; // 4
Test t{3}; // default
cout << t.size << endl; // 3
Test2 move_from_temp = Test2(Test(std::move(t))); // move, move2, destruct
cout << move_from_temp.size << endl; // 5
std::move is static_cast to an rvalue reference type. It does nothing, only a cast to Test && in both versions.
Based on that, in both versions you call the move constructur Test(Test&&).
In the first version you are then done because of mandatory "copy elison" https://en.cppreference.com/w/cpp/language/copy_elision
In the second version you call Test2(Test&&) which cannot be optimized away. The compiler just call the method you provided.

Pointer member is not initialized in copy constructor

In my application
#include <iostream>
class TestClassA
{
public:
int* m_ptr;
TestClassA(int a)
{
m_ptr = new int(a);
std::cout << "Constructor. this: " << this << " m_ptr: " << m_ptr << std::endl;
}
TestClassA(const TestClassA& copy)
{
std::cout << "Copy Constructor. copy: " << &copy << " -> this: " << this << std::endl;
std::cout << "Copy Constructor. old this->m_ptr: " << m_ptr << std::endl;
delete m_ptr; // not initialized pointer
m_ptr = new int;
std::cout << "Copy Constructor. new this->m_ptr: " << m_ptr << std::endl;
*m_ptr = *copy.m_ptr;
}
// passing by value, thus a copy constructor calls first
TestClassA& operator=(TestClassA tmp)
{
std::cout << "Copy assignment " << this << " <- " << &tmp << std::endl;
std::swap(m_ptr, tmp.m_ptr);
return *this;
}
~TestClassA()
{
std::cout << "Destructor " << this << std::endl;
delete m_ptr;
m_ptr = nullptr;
}
};
void testAssignment()
{
TestClassA tca1(1);
std::cout << "tca1.m_ptr: " << tca1.m_ptr << std::endl;
TestClassA tca2(2);
std::cout << "tca2.m_ptr: " << tca2.m_ptr << std::endl;
tca2 = tca1;
}
int main()
{
testAssignment();
return 0;
}
When I call assignment operator receiving arguments by value, copy constructor calls. I guess it is to create a temporary variable and to copy the state of tcs1 to it. The issue is that m_ptr member of this temporary is not initialized, so I can't delete previous m_ptr value to write a new one. What is the proper way of implementing copy constructor in this case?
Copy constructor is a constructor, not an assignment operator. The diffetence is precisely the absence of preexisting resources to destroy. You don't need to destroy anything, just initialize.
The copy constructor is called because you did not make it accept a const reference:
TestClassA& operator=(const TestClassA& tmp)
// ^ ^
It is the tmp parameter that is initialized in the example, not the this of the operator.
Of course, you'll need a local variable to get the swap trick to work, but at least it will be explicit in your code.

Parameterized constructor for assignment

I've noticed some behaviour which I can't understand in parameterized constructors. Given the following program:
#include <iostream>
using namespace std;
class A {
public:
int x;
A() {}
A(int i) : x(i){
cout << "A\n";
}
~A(){
cout << "dA\n";
}
};
int main(){
A p;
p = 3;
cout << p.x << endl;
p = 5;
cout << p.x << endl;
return 0;
}
I get as output:
A
dA
3
A
dA
5
dA
This means that using = triggers the parameterized constructor, destroys the object on which it's called and creates a new object.
I cannot understand this behaviour and I can't find the answer in the standard ( I am sure it is there somewhere, but it may be stated in a sophisticated way). Could someone help me with an explanation?
The phrase you're probably looking for is "implicit conversion".
If you add a copy constructor and an assignment operator, and then give each object a unique ID, it's easier to see where things go:
int counter = 0;
class A {
public:
int id;
A(): id(++counter) {cout << "A(): " << id << "\n";}
A(int i) : id(++counter) {cout << "A(" << i << "): " << id << "\n";}
// Don't copy the id.
// (This isn't used anywhere, but you can't see that it's not used unless it exists.)
A(const A& a) : id(++counter) {cout << "A(" << a.id << "): " << id << "\n";}
// Don't copy the id here either.
A& operator=(const A&a) {cout << id << " = " << a.id << "\n"; return *this;}
~A(){cout << "destroy: " << id << "\n";}
};
int main(){
A p;
cout << "p is " << p.id << "\n";
p = 3;
cout << "p is " << p.id << "\n";
p = 5;
cout << p.id << "\n";
}
Output:
A(): 1
p is 1
A(3): 2
1 = 2
destroy: 2
p is 1
A(5): 3
1 = 3
destroy: 3
1
destroy: 1
As you can see, the parameterized constructor is used to create a temporary object whose value can be assigned to p, and that temporary is destroyed immediately after that.
You can also see that p is alive and well until the very end.
With a statement like
p = 3;
what you're actually doing is
p = A(3);
which really translates to
p.operator=(A(3));
The temporary A object created by A(3) of course needs to be destructed, it is temporary after all.
The object p itself will not be destructed by the assignment.

When I return by value from an assignment operator, firstly what is the mechanism and basis of calling the copy constructor?

Consider this piece of code and its output:
class test {
int a, b;
public:
test (int a, int b) : a(a), b(b) { cout << "Const" << endl;}
test (const test & t) {
cout << "Copy constructor" << endl;
cout << "Being copied to = " << this << " from " << &t << endl;
if (&t == this) {
cout << "returning" << endl;
return;
}
this->a = t.a;
this->b = 6;//t.b;
}
test operator=(const test & in) {
cout << "Assignment operator" << endl;
this->a = in.a;
this->b = in.b;
cout << "Being written to = " << this << " from "<< &in << endl;
return *this;
}
test get () {
test l = test (3, 3);
cout << "Local return " << &l << endl;
return l;
}
void display () {
cout << a << " " << b << endl;
}
};
int main () {
int i = 5, &ref = i;
test t(1,1), u(2,2);
u = t.get();
cout << "u address" << &u <<endl;
//cout << "u ka = " << &u << endl;
u.display();
}
Output:
Const
Const
Const
Local return 0x7fff680e5ab0
Assignment operator
Being written to = 0x7fff680e5a90 from 0x7fff680e5ab0
Copy constructor
Being copied to = 0x7fff680e5a80 from 0x7fff680e5a90
u address0x7fff680e5a90
3 3
I know the way to return from an assignment is by reference, but I was trying to understand how this works since I am a beginner to C++.
What is the address 0x7fff680e5a80 ? Where is this coming from ? Which object is calling the copy constructor here ? I would expect u (address 0x7fff680e5a90) to call it.
Lets follow your code (I modified your constructor to dump the constructed this).
First you isntantiate t and u.
Const at 0x7fffffffdeb0
Const at 0x7fffffffdea0
Then you call t.get, which initializes l.
Const at 0x7fffffffdec0
You then return l.
Local return 0x7fffffffdec0
The copy constructor that would normally happen at this point is elided.
Assignment operator from l to u
Being written to = 0x7fffffffdea0 from 0x7fffffffdec0
Then you are returning the assignment by value (you should return it by reference), so you copy from u to an output value that isn't stored (0x7fffffffde90 from u)
Copy constructor
Being copied to = 0x7fffffffde90 from 0x7fffffffdea0
And then you print u.
u address0x7fffffffdea0
Inside T.
To get rid of the confusing (and unessisary copy) you should return by reference when you do any assignment operator:
test& operator=(const test & in);
Your assignment operator is incorrect:
test operator=(const test & in) {
You should return by reference:
test &operator=(const test & in) {
The object at 0x7fff680e5a80 is a prvalue temporary object of type test that is returned from your (incorrect) assignment operator and immediately discarded:
test operator=(const test & in) {
cout << "Assignment operator" << endl;
this->a = in.a;
this->b = in.b;
cout << "Being written to = " << this << " from "<< &in << endl;
return *this;
} // ^-- copy constructor is called here
You can check this by taking an rvalue reference to the return value of the assignment operator:
auto &&temp = (u = t.get());
std::cout << "Address of temp: " << &temp << std::endl; // prints 0x7fffffffde90

Why constructor is being called twice

I do not understand how constructors work?
Here I have declared an object obj2. It calls constructor abc(), which is perfectly fine.
But when I am assigning
obj2 = 100
Why does compiler allow initializing an integer to a class object? If at all it is allowing then how it is destroying the object and then how it is calling another parameterized constructor.
Now I have another question why destructor is called only once as there are two objects?
One more doubt I have is, compiler is not doing anything with default constructor then why default constructor is required?
class abc{
public:
int a, b;
abc()
{a = 0; b = 0;}
abc(int x)
{a = x;}
~abc()
{std::cout << "Destructor Called\n";}
};
int main()
{
abc obj1;
cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n";
abc obj2;
cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n";
obj2 = 100;
cout << "OBJ2 " << obj2.a << "\n";
system("pause");
return 0;
}
output:
OBJ1 0...0
OBJ2 0...0
Destructor Called
OBJ2 100
But when I am assigning obj2 = 100, how the compiler is allowing initializing an integer to a class object?
This is because when you do the following:
obj2 = 100;
this one will first call abc(int x) to generate an object of the class, then call the default copy assignment operator (since no user-defined is provided) to assign the value 100 to existing obj2. After the assignment, the temporary object is destructed.
If you do not desire this effect, mark the constructor as explict to avoid implicit calls.
explicit abc(int x) {
//do something
}
obj2 = 100;
You defined a constructor which takes an int. This allows for an implicit conversion from int to abc. This requires the creation of a new object. It doesn't just magically set a field in the existing object by calling a constructor; constructors construct new objects.
EDIT: Correct sequence of events from #Steve Jessop
A new instance is created, then copy-assigned to the original, then the temporary (not the original) is destroyed. The copy assignment does indeed magically set both fields in the existing object.
Let us play show and tell, so let us instrument all special members:
#include <iostream>
class abc{
public:
int a, b;
abc()
{ std::cout << "Default constructor\n"; a = 0; b = 0;}
abc(int x)
{ std::cout << "Int constructor\n"; a = x;}
abc(abc const& other): a(other.a), b(other.b)
{ std::cout << "Copy constructor (" << a << ", " << b << ")\n"; }
abc& operator=(abc const& other) {
std::cout << "Assignment operator (" << a << ", " << b << ") = (" << other.a << ", " << other.b << ")\n";
a = other.a;
b = other.b;
return *this;
}
~abc()
{std::cout << "Destructor Called\n";}
};
int main()
{
abc obj1;
std::cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n";
abc obj2;
std::cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n";
obj2 = 100;
std::cout << "OBJ2 " << obj2.a << "\n";
return 0;
}
And we obtain this output:
Default constructor
OBJ1 0...0
Default constructor
OBJ2 0...0
Int constructor
Assignment operator (0, 0) = (100, 0)
Destructor Called
OBJ2 100
Destructor Called
Destructor Called
So, let's reconcile them with line sources:
int main()
{
abc obj1;
// Default constructor
std::cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n";
// OBJ1 0...0
abc obj2;
// Default constructor
std::cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n";
// OBJ2 0...0
obj2 = 100;
// Int constructor
// Assignment operator (0, 0) = (100, 0)
// Destructor Called
std::cout << "OBJ2 " << obj2.a << "\n";
// OBJ2 100
return 0;
// Destructor Called
// Destructor Called
}
You mostly had it all, let us examine the surprises.
First surprise: even though obj2 changes value later abc obj2; will still call the default constructor at the point of declaration.
Second surprise: obj2 = 100 actually means obj2.operator=(abc(100));, that is:
Build a temporary (unnamed) abc from abc(100)
Assign it to obj2
Destroy the temporary before moving on to the next statement
Third surprise: destructors are called at the end of the scope, right before the closing bracket } (and yes, after the return). Since you are using system("pause") I assume you are on Windows => though luck they are called after you end the pause and thus your console Windows disappears in the blink of an eye at the moment they would have appeared. You can either launch the program from a more permanent console, or use an extra scope:
int main () {
{
// your code here
}
system("pause");
return 0;
}
It's because there is constructor which can take an argument of int type. That temporary created object is copied to obj2 by invoking default copy-assignment.
To avoid such conversions, mark constructor as explicit.
You're destructor is called 3 times, you can't see it because of the pause.