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.

Thanks for reading. Any Comments,

Please let me know at adibh1996@gmail.com