Rvalue References

Understand std::move

Because std::move does nothing but cast its argument to an rvalue, there have been
suggestions that a better name for it might have been something like rvalue_cast.

//c++11
template<typename T> // in namespace std
typename remove_reference<T>::type&&
move(T&& param)
{
 using ReturnType = // alias declaration;
 typename remove_reference<T>::type&&;
 return static_cast<ReturnType>(param);
}

// C++14; still in
template<typename T>
decltype(auto) move(T&& param) // namespace std
{
 using ReturnType = remove_reference_t<T>&&;
 return static_cast<ReturnType>(param);
}

Distinguish universal references from rvalue
references.

• If a function template parameter has type T&& for a deduced type T, or if an object is declared using auto&&, the parameter or object is a universal reference.
• If the form of the type declaration isn’t precisely type&&, or if type deduction does not occur, type&& denotes an rvalue reference.
• Universal references correspond to rvalue references if they’re initialized with rvalues. They correspond to lvalue references if they’re initialized with lvalues.

void f(Widget&& param); // rvalue reference
Widget&& var1 = Widget(); // rvalue reference
auto&& var2 = var1; // is a universal reference
template<typename T>
void f(std::vector<T>&& param); // rvalue reference
template<typename T>
void f(T&& param); // is a universal reference
template<typename T>
void f(std::vector<T>&& param); // param is an rvalue reference
std::vector<int> v;
f(v); // error! can't bind lvalue to
 // rvalue reference
auto timeFuncInvocation =
 [](auto&& func, auto&&... params) // C++14
 {
 start timer;
 std::forward<decltype(func)>(func)( // invoke func
 std::forward<decltype(params)>(params)... // on params
 );
 stop timer and record elapsed time;
 };

Use std::move on rvalue references,
std::forward on universal references.

• Apply std::move to rvalue references and std::forward to universal refer‐
ences the last time each is used.
• Do the same thing for rvalue references and universal references being
returned from functions that return by value.
• Never apply std::move or std::forward to local objects if they would other‐
wise be eligible for the return value optimization.

class Widget {
public:
 Widget(Widget&& rhs) // rhs is rvalue reference
 : name(std::move(rhs.name)),
 p(std::move(rhs.p))
 { … }
 …
private:
 std::string name;
 std::shared_ptr<SomeDataStructure> p;
};
class Widget {
public:
 template<typename T>
 void setName(T&& newName) // newName is
 { name = std::forward<T>(newName); } // universal reference
 …
};

using std::move with universal references,that can have the
effect of unexpectedly modifying lvalues

Matrix // by-value return
operator+(Matrix&& lhs, const Matrix& rhs)
{
 lhs += rhs;
 return std::move(lhs); // move lhs into
} 

Matrix // as above
operator+(Matrix&& lhs, const Matrix& rhs)
{
 lhs += rhs;
 return lhs; // copy lhs into
} 

template<typename T>
Fraction // by-value return
reduceAndCopy(T&& frac) // universal reference param
{
 frac.reduce();
 return std::forward<T>(frac); // move rvalue into return
} 

don’t move local variable into return value

Widget makeWidget() // Moving version of makeWidget
{
 Widget w;
 …
 return std::move(w); // move w into return value
} 

Avoid overloading on universal references

std::multiset<std::string> names; // global data structure
void logAndAdd(const std::string& name)
{
 auto now = // get current time
 std::chrono::system_clock::now();
 log(now, "logAndAdd"); // make log entry
 names.emplace(name); // add name to global data
} 
std::string petName("Darla");
logAndAdd(petName); // pass lvalue std::string
logAndAdd(std::string("Persephone")); // pass rvalue std::string
logAndAdd("Patty Dog"); // pass string literal

in the previous example in second and third call name itself is an lvalue, so it’s copied into names.

template<typename T>
void logAndAdd(T&& name)
{
 auto now = std::chrono::system_clock::now();
 log(now, "logAndAdd");
 names.emplace(std::forward<T>(name));
}
std::string petName("Darla"); // as before
logAndAdd(petName); // as before, copy lvalue into multiset
logAndAdd(std::string("Persephone")); // move rvalue instead of copying it
logAndAdd("Patty Dog"); // create std::string in multiset instead of copying a temporary std::string

Constraining templates that take universal references

Alternatives to the combination of universal references and overloading
include the use of distinct function names, passing parameters by lvaluereference-to-const, passing parameters by value, and using tag dispatch.
• Constraining templates via std::enable_if permits the use of universal references and overloading together, but it controls the conditions under which compilers may use the universal reference overloads.
• Universal reference parameters often have efficiency advantages, but they typically have usability disadvantages.

class Person {
public:
 template<
 typename T,
 typename = typename std::enable_if<!std::is_base_of<Person,typename std::decay<T>::type>::value>::type>
 explicit Person(T&& n);
 …
};

//c++14
class Person {
public:
 template<
 typename T,
 typename = std::enable_if_t<!std::is_base_of<Person,std::decay_t<T>>::value>>
 explicit Person(T&& n);
 …
};

#include <type_traits>

template<typename T>
class YourClass {

    YourClass() {
        // Compile-time check
        static_assert(std::is_base_of<BaseClass, T>::value, "type parameter of this class must derive from BaseClass");

        // ...
    }
}
class Person {
public:
    template<typename T,typename = std::enable_if_t<std::is_base_of_v<Person,std::decay_t<T>>>>
    explicit Person(T&& n){
    }
    Person()= default;
};
int main(int argc,char* argv[]){
    Person pp;
    Person p(pp);
    return 0;
}