#include <iostream>
using namespace std;

template< class T > struct my_remove_reference      {typedef T type;};
template< class T > struct my_remove_reference<T&>  {typedef T type;};
template< class T > struct my_remove_reference<T&&> {typedef T type;};
    
template <typename T>
typename my_remove_reference<T>::type&& my_move(T&& arg)
{
    return static_cast<typename my_remove_reference<T>::type&&>(arg);
}
    
struct S
{
    int *ptr = nullptr;
    
    S() { cout << "S() default constructor, ptr = " << ptr << endl; }
    S(int *p) : ptr(p) { cout << "S(int*) constructor, ptr = " << ptr << endl; }
};
    
class MoveTester
{
private:
    int *m_ptr = nullptr;
    
public:
    MoveTester() { cout << "MT() default constructor" << endl; }
    MoveTester(const MoveTester &) = delete;
    MoveTester(MoveTester&& src) : m_ptr(src.m_ptr) { src.m_ptr = nullptr; cout << "MT(MT&&) move constructor" << endl; }
    
    MoveTester(int *ptr) : m_ptr(ptr) { cout << "MT(int*) constructor" << endl; }
    MoveTester(S&& src) : m_ptr(src.ptr) { src.ptr = nullptr; cout << "MT(S&&) move constructor" << endl; }
    
    MoveTester& operator=(const MoveTester &) = delete;
    MoveTester& operator=(MoveTester&& rhs) { m_ptr = rhs.m_ptr; rhs.m_ptr = nullptr; cout << "MT::operator=(MT&&) move assignment" << endl; return *this; }
    MoveTester& operator=(int *rhs) { m_ptr = rhs; cout << "MT::operator=(int*) copy assignment" << endl; return *this; }
    MoveTester& operator=(S&& src) { m_ptr = src.ptr; src.ptr = nullptr; cout << "MT::operator=(S&&) move assignment" << endl; return *this; }
    
    void display(const char *name) const { cout << name << ".m_ptr = " << m_ptr << endl; }
};
    
int* int_to_ptr(intptr_t value) { return reinterpret_cast<int*>(value); }

int main()
{
    cout << "MoveTester mt1;" << endl;
    MoveTester mt1;
    mt1.display("mt1");
    
    cout << endl;

    cout << "MoveTester mt2 = int_to_ptr(12345);" << endl;
    MoveTester mt2 = int_to_ptr(12345);
    mt2.display("mt2");

    cout << endl;
    
    //MoveTester mt3 = mt2; // compiler error! Copy constructor is deleted
    cout << "MoveTester mt3 = my_move(mt2);" << endl;
    MoveTester mt3 = my_move(mt2);
    mt2.display("mt2");
    mt3.display("mt3");

    cout << endl;

    cout << "MoveTester mt4 = S();" << endl;
    MoveTester mt4 = S();
    mt4.display("mt4");

    cout << endl;

    cout << "MoveTester mt5 = S(int_to_ptr(67890));" << endl;
    MoveTester mt5 = S(int_to_ptr(67890));
    mt5.display("mt5");

    cout << endl;
    
    //mt1 = mt5; // compiler error! Copy assignment is deleted
    cout << "mt1 = my_move(mt5);" << endl;
    mt1 = my_move(mt5);
    mt1.display("mt1");
    mt5.display("mt5");

    cout << endl;

    cout << "mt1 = int_to_ptr(13579);" << endl;
    mt1 = int_to_ptr(13579);
    mt1.display("mt1");
    
    cout << endl;

    cout << "mt1 = S();" << endl;
    mt1 = S();
    mt1.display("mt1");
    
    cout << endl;

    cout << "mt1 = S(int_to_ptr(24680));" << endl;
    mt1 = S(int_to_ptr(24680));
    mt1.display("mt1");
    
    return 0;
}