Difference between the move assignment operator and move constructor? - c++

For some time this has been confusing me. And I've not been able to find a satisfactory answer thus far. The question is simple. When does a move assignment operator get called, and when does a move constructor operator get called?
The code examples on cppreference.com yield the following interesting results:
The move assignment operator:
a2 = std::move(a1); // move-assignment from xvalue
The move constructor:
A a2 = std::move(a1); // move-construct from xvalue
So has it do to with which is implemented? And if so which is executed if both are implemented? And why is there the possibility of creating a move assignment operator overload at all, if it's identical anyway.

A move constructor is executed only when you construct an object. A move assignment operator is executed on a previously constructed object. It is exactly the same scenario as in the copy case.
Foo foo = std::move(bar); // construction, invokes move constructor
foo = std::move(other); // assignment, invokes move assignment operator
If you don't declare them explicitly, the compiler generates them for you (with some exceptions, the list of which is too long to be posted here).
See this for a complete answer to when the move member functions are implicitly generated.

When does a move assignment operator get called
When you assign an rvalue to an object, as you do in your first example.
and when does a move constructor operator get called?
When you initialise an object using an rvalue, as you do in your second example. Although it isn't an operator.
So has it do to with which is implemented?
No, that determines whether it can be used, not when it might be used. For example, if there's no move constructor, then construction will use the copy constructor if that exists, and fail (with an error) otherwise.
And if so which is executed if both are implemented?
Assignment operator for assignment, constructor for initialisation.
And why is there the possibility of creating a move assignment operator overload at all, if it's identical anyway.
It isn't identical. It's invoked on an object that already exists; the constructor is invoked to initialise an object which previously didn't exist. They often have to do different things. For example, assignment might have to delete something, which won't exist during initialisation.

This is the same as normal copy assignment and copy construction.
A a2 = std::move(a1);
A a2 = a1;
Those call the move/copy constructor, because a2 doesn't yet exist and needs to be constructed. Assigning doesn't make sense. This form is called copy-initialization.
a2 = std::move(a1);
a2 = a1;
Those call the move/copy assignment operator, because a2 already exists, so it doesn't make sense to construct it.

Move constructor is called during:
initialization: T a = std::move(b); or T a(std::move(b));, where b is of type T;
function argument passing: f(std::move(a));, where a is of type T and f is void f(T t);
Move assignment operation is called during:
function return: return a; inside a function such as T f(), where a is of type T which has a move constructor.
assignment
The following example code illustrates this :
#include <iostream>
#include <utility>
#include <vector>
#include <string>
using namespace std;
class A {
public :
A() { cout << "constructor called" << endl;}
~A() { cout << "destructor called" << endl;}
A(A&&) {cout << "move constructor called"<< endl; return;}
A& operator=(A&&) {cout << "move assignment operator called"<< endl; return *this;}
};
A fun() {
A a; // 5. constructor called
return a; // 6. move assignment operator called
// 7. destructor called on this local a
}
void foo(A){
return;
}
int main()
{
A a; // 1. constructor called
A b; // 2. constructor called
A c{std::move(b)}; // 3. move constructor called
c = std::move(a); // 4. move assignment operator called
a = fun();
foo(std::move(c)); // 8. move constructor called
}
Output :
constructor called
constructor called
move constructor called
move assignment operator called
constructor called
move assignment operator called
destructor called
move constructor called
destructor called
destructor called
destructor called
destructor called

Related

Am I calling the constructor or the copy constructor? [duplicate]

I don't understand the difference between assignment constructor and copy constructor in C++. It is like this:
class A {
public:
A() {
cout << "A::A()" << endl;
}
};
// The copy constructor
A a = b;
// The assignment constructor
A c;
c = a;
// Is it right?
I want to know how to allocate memory of the assignment constructor and copy constructor?
A copy constructor is used to initialize a previously uninitialized object from some other object's data.
A(const A& rhs) : data_(rhs.data_) {}
For example:
A aa;
A a = aa; //copy constructor
An assignment operator is used to replace the data of a previously initialized object with some other object's data.
A& operator=(const A& rhs) {data_ = rhs.data_; return *this;}
For example:
A aa;
A a;
a = aa; // assignment operator
You could replace copy construction by default construction plus assignment, but that would be less efficient.
(As a side note: My implementations above are exactly the ones the compiler grants you for free, so it would not make much sense to implement them manually. If you have one of these two, it's likely that you are manually managing some resource. In that case, per The Rule of Three, you'll very likely also need the other one plus a destructor.)
The difference between the copy constructor and the assignment operator causes a lot of confusion for new programmers, but it’s really not all that difficult. Summarizing:
If a new object has to be created before the copying can occur, the copy constructor is used.
If a new object does not have to be created before the copying can occur, the assignment operator is used.
Example for assignment operator:
Base obj1(5); //calls Base class constructor
Base obj2; //calls Base class default constructor
obj2 = obj1; //calls assignment operator
Example for copy constructor:
Base obj1(5);
Base obj2 = obj1; //calls copy constructor
The first is copy initialization, the second is just assignment. There's no such thing as assignment constructor.
A aa=bb;
uses the compiler-generated copy constructor.
A cc;
cc=aa;
uses the default constructor to construct cc, and then the *assignment operator** (operator =) on an already existing object.
I want know how to allocate memory of the assignment constructor and copy constructor?
IDK what you mean by allocate memory in this case, but if you want to see what happens, you can:
class A
{
public :
A(){ cout<<"default constructor"<<endl;};
A(const A& other){ cout<<"copy constructor"<<endl;};
A& operator = (const A& other){cout <<"assignment operator"<<endl;}
};
I also recommend you take a look at:
Why is copy constructor called instead of conversion constructor?
What is The Rule of Three?
In a simple words,
Copy constructor is called when a new object is created from an existing object, as a copy of the existing object.
And assignment operator is called when an already initialized object is assigned a new value from another existing object.
Example-
t2 = t1; // calls assignment operator, same as "t2.operator=(t1);"
Test t3 = t1; // calls copy constructor, same as "Test t3(t1);"
What #Luchian Grigore Said is implemented like this
class A
{
public :
int a;
A(){ cout<<"default constructor"<<endl;};
A(const A& other){ cout<<"copy constructor"<<endl;};
A& operator = (const A& other){cout <<"assignment operator"<<endl;}
};
void main()
{
A sampleObj; //Calls default constructor
sampleObj.a = 10;
A copyConsObj = sampleObj; //Initializing calls copy constructor
A assignOpObj; //Calls default constrcutor
assignOpObj = sampleObj; //Object Created before so it calls assignment operator
}
OUTPUT
default constructor
copy constructor
default constructor
assignment operator
the difference between a copy constructor and an assignment constructor is:
In case of a copy constructor it creates a new object.(<classname> <o1>=<o2>)
In case of an assignment constructor it will not create any object means it apply on already created objects(<o1>=<o2>).
And the basic functionalities in both are same, they will copy the data from o2 to o1 member-by-member.
I want to add one more point on this topic.
"The operator function of assignment operator should be written only as a member function of the class." We can't make it as friend function unlike other binary or unary operator.
Something to add about copy constructor:
When passing an object by value, it will use copy constructor
When an object is returned from a function by value, it will use copy constructor
When initializing an object using the values of another object(as the example you give).

Why defining move constructor deletes move assignment operator

When i write "f5() = X(33);"(when move constructor in comment line) the compiler does not throw errors.
But when I add a Move Constructor compiler says:
" 'X &X::operator =(const X &)': attempting to reference a deleted function "
is move constructor delete assignment operator ??
#include <iostream>
class X {
int* p;
public:
X(int ii = 0) { p = new int(ii); };
X(const X& obj) { this->p = new int(*(obj.p)); };
// X(X&& obj) { this->p = new int(*(obj.p)); delete obj.p; obj.p=nullptr;
};
X f5() {
return X(5);
}
int main() {
f5() = X(33);
system("pause");
}
f5() = X(33);
would be calling the assignment operator, because an X object already exists there. It's an assignment, not a construction. Before you had a move constructor, it was calling an auto-generated assignment operator.
Now, the compiler is unwilling (well, forbidden) to generate a default assignment operator because you implement a move constructor. The logic goes, you probably know better about your type, so it will let you implement
X &X::operator =(const X &)
The error message ('X &X::operator =(const X &)': function was implicitly deleted because 'X' has a user-defined move constructor) clearly answers "yes" to your question. But you apparently want to know the motivation for this rule in C++.
The rule was introduced in C++11 (together with move semantics) to enforce the "Rule of 3/5", which says that all of the following must be used together or not at all:
copy constructor
copy assignment operator
move constructor
move assignment operator
destructor
(in some situations, it is ok to omit move or copy aiir of constructor/assignment).
Similarly, it would make sense to delete the copy assignment operator when a copy constructor is defined, but it could not be done in 2011 to preserve backward compatibility.
Your code is a good illustration of why the Rule of 3/5 is needed. As is, your class leaks memory. If you add a destructor with delete p, but don't define a move assignment operator, then with your example you get a double deletion (undefined behavior, likely crash).
Finally, note that your move constructor is wrong.

Default move assignment calls destructor, copy assignment doesn't

I'm observing a strange behavior I don't quite understand with respect to destructors and (default) copy and move assignments.
Let's say I have a class B that has default everything and a class Test that has custom destructor, default copy assignment, and (potentially) default move assignment.
Then we create an instance of B, assign it to a variable, and replace with a new instance using assignment (where the right side is rvalue).
Two things seems weird to me and I can't see the reason for them in documentation.
When Test doesn't have move assignment (thus its copy assignment is called) the destructor of T1 object isn't explicitely called. I assume that in this case the idiomatic thing to do is to clean the resources as part of the copy assignment. Why is it different when move assignment is there (and called), however? If its there the Test's destructor is called explicitely (?by the operator).
The documentation specifies that the other after move assignment can be left in whatever state. How come the destructor for T2's temporal rvalue (i.e. the right side of =B("T2")) isn't called in case B's member doesn't have move assignment?
Playground code: https://onlinegdb.com/S1lCYmkKOV
#include <iostream>
#include <string>
class Test
{
public:
std::string _name;
Test(std::string name) : _name(name) { }
~Test()
{
std::cout << "Destructor " << _name << std::endl;
}
Test& operator=(const Test& fellow) = default;
//Test & operator= ( Test && ) = default;
};
class B {
public:
Test t;
B() : t("T0") {}
B(std::string n) : t(n) {}
};
int fce(B& b)
{
std::cout << "b = B(T2)\n";
b = B("T2");
std::cout << "return 0\n";
return 0;
}
int main() {
B b("T1");
std::cout << "fce call\n";
fce(b);
std::cout << "fce end " << b.t._name << std::endl;
}
Output with move:
fce call
b = B(T2)
Destructor T1
return 0
fce end T2
Destructor T2
Output without move:
fce call
b = B(T2)
Destructor T2
return 0
fce end T2
Destructor T2
Default move assignment calls destructor, copy assignment doesn't
Both assignments result in the destruction of a temporary B object, so the destructor is called.
replace with a new instance using assignment
Pedantic note: Assignment doesn't replace instances. The instance remains the same; the value of the instance is modified. This distinction may be subtle, but may also be relevant to your confusion.
When Test doesn't have move assignment (thus its copy assignment is called) the destructor of T1 object isn't explicitely called.
It's somewhat unclear what you mean by "T1 object". The variable b that you initialised with "T1" is destroyed. But when it is destroyed, it's value has previously been assigned to "T2", so that's what the destructor inserts into cout. This happens in both move and copy cases, and this is the second Destructor TX line in the output.
Why is it different when move assignment is there (and called), however?
The difference is when the temporary object from the line b = B("T2") is destroyed. This is the first Destructor TX line in the output.
After copy assignment, this temporary will still hold the "T2" value, so that's what you see in the destructor.
After move assignment, the temporary is no longer guaranteed to contain "T2", but rather it is left in a valid but unspecified state (as described in specification of std::string), so output could be anything. In this case it happened to be "T1". (Based on this result, we might make a guess that the move assignment operator of the string may have been implemented by swapping the internal buffers. This observation is not a guaranteed behaviour).
The documentation specifies that the other after move assignment can be left in whatever state. How come the destructor for T2's temporal rvalue (i.e. the right side of =B("T2")) isn't called in case B's member doesn't have move assignment?
The destructor of the temporary is called. The temporary simply is no longer in the state described by "contains "T2"" after it has been moved from.

Rvalue references and constructors

I read the following article about rvalue references http://thbecker.net/articles/rvalue_references/section_01.html
But there are some things I did not understand.
This is the code i used:
#include <iostream>
template <typename T>
class PointerHolder
{
public:
// 1
explicit PointerHolder(T* t) : ptr(t)
{
std::cout << "default constructor" << std::endl;
}
// 2
PointerHolder(const PointerHolder& lhs) : ptr(new T(*(lhs.ptr)))
{
std::cout << "copy constructor (lvalue reference)" << std::endl;
}
// 3
PointerHolder(PointerHolder&& rhs) : ptr(rhs.ptr)
{
rhs.ptr = nullptr;
std::cout << "copy constructor (rvalue reference)" << std::endl;
}
// 4
PointerHolder& operator=(const PointerHolder& lhs)
{
std::cout << "copy operator (lvalue reference)" << std::endl;
delete ptr;
ptr = new T(*(lhs.ptr));
return *this;
}
// 5
PointerHolder& operator=(PointerHolder&& rhs)
{
std::cout << "copy operator (rvalue reference)" << std::endl;
std::swap(ptr, rhs.ptr);
return *this;
}
~PointerHolder()
{
delete ptr;
}
private:
T* ptr;
};
PointerHolder<int> getIntPtrHolder(int i)
{
auto returnValue = PointerHolder<int>(new int(i));
return returnValue;
}
If I comment constructors 2 and 3, the compiler says :
error: use of deleted function ‘constexpr PointerHolder<int>::PointerHolder(const PointerHolder<int>&)’
auto returnValue = PointerHolder<int>(new int(i));
^
../src/rvalue-references/move.cpp:4:7: note: ‘constexpr PointerHolder<int>::PointerHolder(const PointerHolder<int>&)’ is implicitly declared as deleted because ‘PointerHolder<int>’ declares a move constructor or move assignment operator
But If I uncomment any one of the two, it compiles and the execution yields the following :
default constructor
So these are my questions :
When the constructors 2 and 3 where commented, it tried to call the constructor 2. Why ? I would expect it to call the constructor 1, which is what it did when i uncommented them !
Regarding the error : I declared a "move" copy operator, which means that my object can be "move" copied from a rvalue reference. But why does it implicitly delete my normal copy constructor ? And if so why does it allow me to "undelete" it by defining it explicitly and use it ?
When the constructors 2 and 3 where commented, it tried to call the constructor 2. Why ?
Because your declaration initialises returnValue from a temporary object - that temporary needs to be movable or copyable, using a move or copy constructor. When you comment these out, and inhibit their implicit generation by declaring a move-assignment operator, they are not available, so the initialisation is not allowed.
The actual move or copy should be elided, which is why you just see "default constructor" when you uncomment them. But even when elided, the appropriate constructor must be available.
why does it implicitly delete my normal copy constructor ?
Usually, if your class has funky move semantics, then the default copy semantics will be wrong. For example, it might copy a pointer to an object which is only supposed to be pointed to by a single instance of your class; which might in turn lead to double deletion or other errors. (In fact, your move constructor does exactly this, since you forgot to nullify the argument's pointer).
It's safer to delete the copy functions, and leave you to implement them correctly if you need them, than to generate functions which will almost certainly cause errors.
And if so why does it allow me to "undelete" it by defining it explicitly and use it ?
Because you often want to implement copy semantics as well as move semantics.
Note that it's more conventional to call 3 a "move constructor" and 5 a "move-assignment operator", since they move rather than copy their argument.
Because you're deleting the copy constructor and the line
auto returnValue = PointerHolder<int>(new int(i));
isn't a real assignment, it invokes a copy constructor to build the object. One of the two copy constructors (either by reference or by rvalue) needs to be available in order to succeed in initializing the object from that temporary. If you comment those both out, no luck in doing that.
What happens if everything is available? Why aren't those called?
This is a mechanism called "copy elision", basically by the time everything would be properly available to "initialize" returnValue with a copy-constructor, the compiler's being a smartboy and realizing:
"oh, I could just initialize returnValue like this"
PointerHolder<int> returnValue(new int(i));
and this is exactly what happens when everything is available.
As for why the move constructor seems to overcome the implicit copy-constructor, I can't find a better explanation than this: https://stackoverflow.com/a/11255258/1938163
You need a copy or move constructor to construct your return value.
If you get rid of all copy/move constructors and all assignment operators (use default generated constructors/operators) the code will compile, but fail miserably due to multiple deletions of the member ptr.
If you keep the copy/move constructors and assignment operators you might not see any invocation of a constructor, due to copy elision (return value optimization).
If you disable copy elision (g++: -fno-elide-constructors) the code will fail again due to multiple deletions of the member ptr.
If you correct the move-constructor:
// 3
PointerHolder(PointerHolder&& rhs) : ptr(0)
{
std::swap(ptr, rhs.ptr);
std::cout << "copy constructor (rvalue reference)" << std::endl;
}
And compile with disabled copy elision the result might be:
default constructor
copy constructor (rvalue reference)
copy constructor (rvalue reference)
copy constructor (rvalue reference)

copy constructor problem while reading an object directly into vector using copy and stream iterators

can some body please explain the behavior in the output while running the following code.
I am little confused about number of times the copy constructor getting called.
using namespace std;
class A {
int i;
public:
A() {
};
A(const A& a) {
i = a.i;
cout << "copy constructor invoked" << endl;
};
A(int num) {
i = num;
};
A& operator = (const A&a) {
i = a.i;
// cout << "assignment operator invoked" << endl;
};
~A() {
cout << "destructor called" << endl;
};
friend ostream& operator << (ostream & out, const A& a);
friend istream& operator >> (istream &in, A&a);
};
ostream & operator << (ostream &out, const A& a) {
out << a.i;
return out;
}
istream & operator >> (istream & in, A&a) {
in >> a.i;
return in;
}
int main() {
vector<A> vA;
copy(istream_iterator<A>(cin), istream_iterator<A>(), back_inserter(vA));
// copy(vA.begin(), vA.end(), ostream_iterator<A>(cout, "\t"));
return 0;
}
The output observed is
ajay#ubuntu:~/workspace/ostream_iterator/src$ ./a.out
40
copy constructor invoked
copy constructor invoked
copy constructor invoked
copy constructor invoked
copy constructor invoked
copy constructor invoked
copy constructor invoked
copy constructor invoked
copy constructor invoked
copy constructor invoked
copy constructor invoked
destructor called
destructor called
destructor called
destructor called
destructor called
destructor called
destructor called
destructor called
destructor called
destructor called
destructor called
destructor called
destructor called
ajay#ubuntu:~/workspace/ostream_iterator/src$
I thought the copy constructor would be called once while inserting into the vector, as containers stores objects by values.
The istream_iterator is getting built and copied a bunch of times. Here's my output:
40
copy constructor invoked
copy constructor invoked
copy constructor invoked
copy constructor invoked
destructor called
destructor called
destructor called
destructor called
copy constructor invoked
copy constructor invoked
destructor called
copy constructor invoked
copy constructor invoked
destructor called
copy constructor invoked
copy constructor invoked
copy constructor invoked
copy constructor invoked
copy constructor invoked
copy constructor invoked
copy constructor invoked
The copy constructor should be called at least 3 times:
1 copy per istream_iterator passed to std::copy, as
istream_iterator stores a local
value of the parameterized type and
most likely copies it in its copy
constructor and they are passed by
value to std::copy. (2)
1 to insert into vector<A> vA via vA.push_back(...), called by
the back_inserter(vA) assignment
operator. (3)
The compiler can probably optimize out a few of those copies, but where the other 8 come from is a mystery to me.
EDIT: Incidentally, I get 8 calls to the copy constructor running on codepad.org: http://codepad.org/THFGFCCk
EDIT2: When I ran this with VS2008, I got 7 calls to the copy constructor. The additional 4 were the result of copying around the input iterators to std::copy for bounds checking in a debug build. When running in a fully optimized build, I only get 3 calls to the copy constructor.