#include <string>
#include <iostream>
#include <iomanip>
#include <utility>
using namespace std;

// Wrapper for heap-allocated double
struct D {
    D(double x = -1.0) : p{new double(x)} { cout << "(ctor:" << *p << ") "; }
    ~D() { cout << "(dtor:" << (p ? *p : 0) << ") "; delete p; }

    D(const D& d) : p{new double(*d.p)} { cout << "(copy:" << *p << ") "; }
    D(D&& d) : p{d.p} { d.p = nullptr; cout << "(move:" << *p << ") "; }

    D& operator=(const D& d) = delete;
    D& operator=(D&& d) = delete;

    operator double&() { return *p; }
    operator const double&() const { return *p; }

    double* p;
};

int main() {
    //////////////////////////////////////////////////////////////////
    // 1. Binding lvalue
    //

    D d1 { 1.0 };
    D& rd1 = d1;
    const D& crd1 = d1;
    // D&& rrd1 = d1; // err: cannot bind lvalue to rvalue reference

    //////////////////////////////////////////////////////////////////
    // 2. Binding rvalue
    //

    // D& rd2 = D(2.0); // err: cannot bind rvalue to lvalue reference
    const D& crd3 = D(3.0);
    D&& rrd4 = D(4.0);
    // the temp object is mutable through rvalue ref
    rrd4 += 0.1; 
    // and both temp objects are still alive!
    cout << "[" << crd3 << "," << rrd4 << "] ";

    //////////////////////////////////////////////////////////////////
    // 3. Binding rvalue reference to another rvalue reference
    //

    // D&& rrd4_1 = rrd4; // err: rvalue reference itself is an lvalue!

    D&  rrd4_2 = rrd4; // ok: rvalue reference itself is an lvalue!
    D&& rrd4_3 = std::move(rrd4); // ok: cast rrd4 back to rvalue again

    //////////////////////////////////////////////////////////////////
    // 4. Summary of lvalue & rvalue
    //
    // * lvalue
    //     - named variables, function call returning by reference, etc.
    //     - ex) i, s[i]  (assuming i is int, and s is std::string)
    //
    // * rvalue
    //     - literals, function call returning by value, unnamed temp object,
    //       cast expression to rvalue reference, etc.
    //     - two sub-categories:
    //         1) prvalues - ex) 2.0, s + s, string("abc")
    //         2) xvalues  - ex) std::move(s)
    //
    // Some surprises:
    //     - glvalue (generalized lvalue) is the union of lvalue and xvalue
    //     - rvalue reference itself is an lvalue because it has a name!
    //     - C string literals (ex. "abc") are lvalues
    //

    cout << "(bye) ";
}
