Move Semantics - Reasons to use and differences to Copy
adib 11/22/2020
Previously when we do copy assignment and copy constructor, simply copy the values from one object to another existing object. Now there is a choice in C++ for those who wants to avoid the copy mechanism used in copy constructor.And its called Move Construction.
Actually when you push_back() on a vector object or any container, you're passing an rvalue reference.
std::vector<int> v1; // passing some value (rvalue) to a vector object v1.push_back(23,34,65);
Move mechanism is about avoiding the copying of src obj to the dst object if its unnecessary. But if there is a need to do copy assignment and construction, it isn't that much expensive in relation to the simplicity of the project and abundance of resources.
We move the values from one object to another existing object. By overloading operator =, not so that it takes an lvalue reference, like in copy assignment, but so that it takes an rvalue reference.
Although if there's a need for definging how Move assignment is done, here's an example:
class Test { int var1; Test& operator= (Test&& prop) { this->var1 = prop.var1; // for example here we define moving // like giving that property 0 value. other.a = 0; // although we can null it out(N.W code) other.a = nullptr; // or we can assign a string(value) // telling the one who is // watching on this property // what happened to this property other.a = "moved"; return *this; } };
There are 5 situations in which an object is being copyed or moved:
As the source of an assignment
As an object initializer
As a function argument
As a function return value
As an exception
Here is a simple definiton of each of these states:
1.As the source of an assignment
This happens whenever you assign a value to an object directly using assingment operator. Whether it is for class objects or even normal variables. The value stored in that object(the source object) will be copied or moved due to instructions made there to destination object.
2.As an object initializer
This is by using either copy constructor or move constructor. This is a special form of first examination; meaning that the copying or moving is done when you instantiate an object from a class.
3.As a function argument
This is a common one when you pass some of your variables to a function, and there values are replaced. When calling a function, values from variables that you define in main(not exactly meaning just main function) function will be copied or moved to objects in that function definition.
4.As a function return value
This is when you return something from a function. When a function returns a value, its value will be copied or moved to whatever calles that function.
5.As an exception
This one happens when an exception is thrown and in your program is used to help something in your program in someway. For example assume you have a function that somewhere inside this function an exception is thrown. This exception is being copied to where we've done catch(exception e) object, or if don't catch it, it will be copy to another object to maintain exceptions and then throw to the face of user in runtime.
By having an overload of the object's constructor below, we made a move constructor:
class account { public: // member varialble for sizing int siz; // default ctor account() = default; // overloaded constructor for rvalue references account(account &&other) { this->siz = other.siz; // this is the difference between copy constructor and move constructor other.siz = 0; } };
What is Temporary in C++
The very first assumption in our minds about temporary is values like "1,2,3" or "a,b,c" and ... . But there is a more carefully done definition for temporaries in C++.
Anything in C++ that its resources can be looted is Temporary. Not a hundred percent matching what we have in mind about temporaries, but its more realistic in C++ world(maybe programming world).
// lvalue reference int var1 = 9; int& var2 = var1; // rvalue reference assigned to another rvalue reference made by compiler int&& var3 = var2 + var2;
Some notes About std::move
1.A move constructor is supposed to remove the value from its argument.
2.A move operator is applied when a rvalue reference is used as an initializer or as the RHS of an assignment.
3.std::move does not actually move anything.instead it returns a reference to its argument.
Whenever you call std::move, the move constructor of that object will be called.
// source object to be moved account a; // set its properties if needed a.siz = 1; // moving a using std::move // calling B(B&& other); account b (std::move(a)); // check out results cout << a.siz << endl; //0 cout << b.siz << endl; //1
What happens after moving an object?
Watch out that moving an object, it is not in a determinate state for pushing it into a determinate state, We must reassign it with a value.
For example, a move constructor of a vector might copy(move) the pointer to the head of the vector object and store nullptr in the argument instead of allocating and copying individual elements.
Some notes about moving and rvalues
1.Another differentiation for rvalue and lvalue is whether you can achieve their address or not?
2.Rvalue references cannot be initialized with lvalues. and there is an exception. and its when const lvalue reference can be assign an rvalue.
3.Named objects are lvalue.
4.never return a rvalue reference.because you'll catch a reference that is not useful when you go out of the scope of the function.
#include <iostream> #include <vector> using namespace std; // Note that here we pass args by pointer int& func(int* a, int* b) { // modify those 2 values that this function is referring *a = 76; *b = 49; // returning the content of one of those referrals return *a; } int main() { // declaring 2 values that will modify in our application auto var1 = 4; auto var2 = 3; // calling a function that will return a lvalue reference auto res = func(&var1, &var2); // now if want to modify var1 var1 = 45; // here if want to access the result of what func() has returned, // we achieve the last time content when func() is executed cout << "The content of result is " << res << endl; // checking values of var1 and var2 // (var1 is not anymore in under our control) cout << "Now var1 and var2 are changed :" << endl << "var1 equals = " << var1 << " and var2 equals = " << var2 << endl; }
5.A move constructor is supposed to remove the value from its argument.
6.A move operator is applied when an rvalue reference is used as an initializer or as the RHS of an assignment.