Vector with move constructor and move assignment operator
Default constructor

    MyVector():m_Size(0), m_pInt(nullptr)
    {   }


Stop trying to save space like that. THe point of good coding is to make it readable by humans. The computer can read any style so try and make it more maintainable by making it readable.

I would write it like this:

    MyVector()
        : m_Size(0)
        , m_pInt(nullptr)
    {}


Sure you can use nullptr for the zero sized array. But this seems like a premature optimization. You can make the rest of your code less complicated by never having a null m_pInt. If there is never a null then your code does not need to check for it.

Also currently you have different behavior for:

MyVector  x;
MyVector  x(0);


Constructor

    MyVector(int x)
       // Use the initializer list to initialize the elements.
    {
        m_Size = x;
        m_pInt = new int[m_Size];
        for (int i = 0; i &lt; m_Size; ++i)
        {
            m_pInt[i] = i;
        }
    }


Print

Sure this works. But std::cout is not the only stream you way want to print too

    void print() const
    {
        for (int i = 0; i &lt; m_Size; ++i)
        {
            cout &lt;&lt; m_pInt[i] &lt;&lt; endl;
        }
    }


So I would pass a stream to print(). It can default to std::cout. Then you should add an operator&lt;&lt;. As the normal way of printing something is via &lt;&lt;.

  MyVector  d(15);
  std::cout &lt;&lt; d;


Note: prefer "\n" to std::endl. The difference is a stream flush. The stream will already flush itself when required. So you adding extra flushes is only going to cause the flushes to be less optimal.

Copy Constructor

    MyVector(const MyVector&amp; rhs)
        // Use the initializer list
    {
        m_Size = rhs.m_Size;
        m_pInt = new int[m_Size];
        for (int i = 0; i &lt; m_Size; ++i)
        {
            m_pInt[i] = rhs.m_pInt[i];
        }
    }


Move constructor

    MyVector(MyVector&amp;&amp; rhs):m_Size(move(rhs.m_Size)), m_pInt(move(rhs.m_pInt))
    {
        rhs.m_Size = 0;
        rhs.m_pInt = nullptr;
    }


Yes that works. But you should also mark the move constructor as noexcept. The standard containers have optimizations that can be applied if they know tour class will not throw when being moved. Otherwise they have to fallback to copying to provide the strong exception guarantee.

The standard technique is to use swap though. It makes it look and behave just like move assignment. See below.

Copy Assignment

Yes the test for self assignment looks like a good optimization.

    MyVector&amp; operator=(const MyVector&amp; rhs)
    {
        if (this != &amp;rhs)
        {


BUT its not. Self assignment happens so rarely (in fact basically never in real code) that what you are doing is pesimizing the normal flow and as a result will make your code slower. You do need to cope with self assignment but because it is so rare you don't need to worry that it is not the optimal path.

The second issue I have here is that you destroy the local data before you have a copy of the new data ready.

            if (m_pInt != nullptr)
            {
                delete m_pInt;
            }


If something goes wrong in the rest of your code then you will be unable to roll back the state and thus can not provide the strong exception guarantee. When copying an object it should happen in three distict phases.

1. Copy the source into a temporary object.                  This is dangerous as exceptions can happen. So you must do this without changing the state of the current object.
2. Swap the state of the current object with the temporary.  Swapping is supposed to be exception safe. So you can safely change the state of your object.
3. Now you can release the old resources.                    This is unlikely to go wrong; But even if something goes wrong your object is in a good state.


So your Copy assignment should look like this:

MyVector&amp; operator=(const MyVector&amp; rhs)
{
    // Make a copy to temp
    std::size_t tSize = rhs.m_Size;
    int*        tInt  = new int[tSize];
    std::copy(rhs.m_pInt, rhs.m_pInt + rhs.m_Size, tInt);

    // Now swap with your current object state.
    std::swap(tSize, m_Size);
    std::swap(tInt,  m_pInt);

    // now destroy the old data;
    delete [] tInt;

    // Done
    return *this;
}


If you look carefully at those three stages. Stage 1 looks like the constructor and stage 3 looks like the destructor and stage 2 looks like a standard swap function so we can simplify the above to exactly that:

MyVector&amp; operator=(const MyVector&amp; rhs)
{
    MyVector  temp(rhs);        // 1. Copy
    temp.swap(*this);           // 2. Swap

    // Done
    return *this;
}                               // 3. delete done by destructor.


This is called the copy and swap idiom.

Move Assignment

Again the pesimizing test for self assignment.

    MyVector&amp; operator=(MyVector&amp;&amp; rhs)
    {
        if (this != &amp;rhs)


The standard move assignment is to swap the source and the destination. This has a couple of benefits.


You don't call delete (and thus don't invoke the destructor). Thus it is potentially faster.
Because you did not delete the data there is an opportunity for it to be reused. 
If the source is going out of scope it will invoke its destructor and destroy your data but it will be done after the completion of your object thus giving you strong exception guarantee. Thus allowing you to make your assignment operator noexcept.


Standard Move Assignment

MyVector&amp; operator=(MyVector&amp;&amp; rhs) noexcept
{
    rhs.swap(*this);
    return *this;
}


I wrote a series of posts about all this.

Vector - Resource Management Allocation
Vector - Resource Management Copy Swap
Vector - Resize
Vector - Simple Optimizations
Vector - the Other Stuff

Re-Write

class MyVector {
    size_t m_Size;
    int*   m_pInt;
public:
    ~MyVector()
    {
        delete[] m_pInt;
    }

    MyVector(int x = 0)
        : m_Size(x)
        , m_pInt(new int[m_Size])
    {
        for (int i = 0; i &lt; m_Size; ++i)
        {
            m_pInt[i] = i;
        }
    }

    void print(std::ostream&amp; str = std::cout) const
    {
        for (int i = 0; i &lt; m_Size; ++i)
        {
            str &lt;&lt; m_pInt[i] &lt;&lt; "\n";
        }
    }
    friend std::ostream&amp; operator&lt;&lt;(std::ostream&amp; str, MyVector const&amp; data)
    {
        data.print(str);
        return str;
    }

    MyVector(const MyVector&amp; rhs)
        : m_Size(rhs.m_Size)
        , m_pInt(new int[m_Size])

    {
        std::copy(rhs.m_pInt, rhs.m_pInt + rhs.m_Size, m_pInt);
    }

    MyVector(MyVector&amp;&amp; rhs) noexcept
        : m_Size(0)
        , m_pInt(nullptr)
    {
        rhs.swap(*this);
    }

    MyVector&amp; operator=(MyVector const&amp; rhs)
    {
       MyVector  t(rhs);
       t.swap(*this);
       return *this;
    }

    MyVector&amp; operator=(MyVector&amp;&amp; rhs) noexcept
    {
        rhs.swap(*this);
        return *this;
    }

    void swap(MyVector&amp; other) noexcept
    {
        using std::swap;
        swap(m_Size, other.m_Size);
        swap(m_pInt, other.m_pInt);
    }
    friend void swap(MyVector&amp; lhs, MyVector&amp; rhs)
    {
        lhs.swap(rhs);
    }
};