Universal reference and Perfect forwarding
==========================================



Universal refrence is used in case of type deduction.



class X;


void f(X&& param);      // rvalue reference


X&& var1 = X();         // rvalue reference


auto&& var2 = var1;     // NOT rvalue reference: universal reference


template <typename T>
void f(std::vector<T>&& param); // rvalue reference (1)


template <typename T>
void f(T&& param);       // NOT rvalue reference: universal reference (2)


template <typename T>
void f(const T&& param); // rvalue reference



X var;

f(var);                 // lvalue passed: param type is: X& (2)

f(std::move(var));      // rvalue passed: param type is: X&& (2)



std::vector<int> v;

f(v);           // syntax error: can't bind lvalue to rvalue  (1)




template <class T, class Allocator = allocator<T>>
class vector
{
public:
  void push_back(T&& x);  // rvalue reference, no type deduction here
  // ...
};




template <class T, class Allocator = allocator<T>>
class vector
{
public:
  template <class... Args>
  void emplace_back(Args&&... args);    // universal reference, type deduction 
  // ...
};




Perfect forwarding
==================




template<typename T, typename Arg>
shared_ptr<T> factory(Arg arg)
{
  return shared_ptr<T>(new T(arg));
}



Unfortunately, this will call T(arg) by value.
A half-good solution is passing arg by reference:


template<typename T, typename Arg>
shared_ptr<T> factory(Arg& arg)
{
  return shared_ptr<T>(new T(arg));
}


But this does not work for rvalue parameters:

factory<X>(f());        // error if f() returns by value
factory<X>(42);         // error


So we can add an overloaded factory function:

template<typename T, typename Arg>
shared_ptr<T> factory(const Arg& arg)
{
  return shared_ptr<T>(new T(arg));
}


Here is the issue:

-- If factory has many arguments, we have to provide all combinations
   of const and non-const references.

-- arg parameter inside factory() is an lvalue --> no move semantics





Reference collapsing
====================



In pre-C++11 no way to set a reference to a reference: A& & is a compile error
In C++11 we have "reference collapsing" rule:


  A& &   --> A&
  A& &&  --> A&
  A&& &  --> A&
  A&& && --> A&&




template <typename T>
void f(T&&);            // special template argument deduction rule

- If f() called on "lvalue of A"  T --> A&     argument type --> A&
- If f() called on "rvalue of A"  T --> A      argument type --> A



template<typename T, typename Arg>
shared_ptr<T> factory(Arg&& arg)
{
  return shared_ptr<T>(new T(std::forward<Arg>(arg)));
}
template<class S>
S&& forward(typename remove_reference<S>::type& a) noexcept
{
  return static_cast<S&&>(a);
}




Examples:

X x;
factory<A>(x);         Arg --> X&

X& && forward(remove_reference<X&>::type& a) noexcept
{
  return static_cast<X& &&>(a);
}
shared_ptr<A> factory(X& && arg)
{
  return shared_ptr<A>(new A(std::forward<X&>(arg)));
}

which becomes:

shared_ptr<A> factory(X& arg)
{
  return shared_ptr<A>(new A(std::forward<X&>(arg)));
}
X& std::forward(X& a)
{
  return static_cast<X&>(a);
}


X foo();
factory<A>(foo());      Arg --> X

shared_ptr<A> factory(X&& arg)
{
  return shared_ptr<A>(new A(std::forward<X>(arg)));
}

X&& forward(X& a) noexcept
{
  return static_cast<X&&>(a);
}


std::forward --> keeps move semantic

std::remove_reference<S>::type&  == S&




template <typename T>           // C++11
typename remove_reference<T>::type&&
std::move(T&& a) noexcept
{
  typedef typename remove_reference<T>::type&& RvalRef;
  return static_cast<RvalRef>(a);
}



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


std::move(x)   <===>   static_cast<X&&>(x)