#include using namespace std; class A { public: A(int ii) { i=ii; } void print(ostream &os) const { os << i; } private: int i; }; // none of these versions of f take advantage of the difference // in type, but the idea is that you can void f(const A &a) { cout << "called f, version 1" << endl; // in here, a cannot be changed! // guarenteed to be a const l-val reference a.print(cout); cout << endl; } void f(A &&a) { cout << "called f, version 2" << endl; // in here, a can be changed (and we know it isn't used // again later) // a guarenteed to be an r-val reference a.print(cout); cout << endl; } void f(int i) { cout << "called f, version 3" << endl; cout << i << endl; } A makeA(int i) { return A(i); } // no matter how this is called, it will always invoke the first // version of f /* void g(const A &a1, const A &a2) { f(a1); f(a2); } */ template void g(T1 &&a1, T2 &&a2) { // in here, a1 might be a rval ref, but it might be an lval ref! // same for a2 // this is due to reference collapsing // (note: reference collapsing also happens with "auto &&") f(forward(a1)); // calls f with the same // lvalueness/rvalueness/constness that g was called with f(forward(a2)); } // what cannot be perfect forwarded? // a few things, most notably // NULL (so use nullptr!), and // initializer lists (sadly) int main(int argc, char **argv) { A a(1); f(a); f(makeA(2)); f(3); cout << endl; g(a,a); cout << endl; g(a,makeA(2)); cout << endl; g(makeA(2),makeA(2)); cout << endl; g(a,3); cout << endl; g('a',"string"); }