//rvalue classes and move function
template <class T>
struct r_ref {
    T& ref;
    r_ref(T& rhs) :ref(rhs) {}
    r_ref(const r_ref& rhs) :ref(rhs.ref) {}
};
template <class T>
struct r_val : r_ref<T> {
    mutable T val;
    r_val(T& rhs) :r_ref<T>(val), val(rhs) {}
    r_val(const r_ref<T>& rhs) :r_ref<T>(val), val(rhs) {}
};
template <class T>
r_ref<T> move(T& rhs) {return r_ref<T>(rhs);}





//demo classes
#include <iostream>
#include <string>
class demo {
public:
    std::string data;
    demo() : data("LONG STRING SO NO CHEATS") {std::cout<<"default construct ";}
    demo(const demo& rhs) : data(rhs.data.begin(),rhs.data.end()) {std::cout<<"copy construct ";}
    demo(const r_ref<demo>& rhs) : data("MOVED FROM NO CHEATS") {data.swap(rhs.ref.data); std::cout<<"move construct ";}
    demo& operator=(const demo& rhs) {data.assign(rhs.data.begin(),rhs.data.end()); std::cout<<"copy assignment "; return *this;}
    demo& operator=(const r_ref<demo>& rhs) {data.swap(rhs.ref.data); std::cout<<"move assignment "; return *this;}
    ~demo() {std::cout<<"destruct ";}
    void prove() const {std::cout<<(void*)data.c_str()<<' '<<data.c_str();}
};

r_val<demo> function(demo rhs) {
    std::cout << "\nwant move assignment: ";
    demo obj(move(rhs));
    std::cout << "\nwant VAR1: ";
    obj.prove();
    std::cout << "\nwant move construct, destruct, destruct, move construct, destruct:\n\t";
    return move(obj);
}



//test suite
int main() {
   std::cout << "\nwant default construct: ";   demo a;
   std::cout << "\nwant VAR1: ";                a.prove();
   std::cout << "\nwant move construct: ";      demo b = function(move(a));   
   std::cout << "\nwant VAR2: ";                a.prove();
   std::cout << "\nwant VAR1: ";                b.prove();
   std::cout << "\nwant copy assignment: ";     a = b;
   std::cout << "\nwant VAR3: ";                a.prove();
   std::cout << "\nwant VAR1: ";                b.prove();
   std::cout << "\nwant move assign: ";         a = move(b);
   std::cout << "\nwant VAR1: ";                a.prove();
   std::cout << "\nwant VAR3: ";                b.prove();
   std::cout << "\nwant 2x destruct: ";
}
    