Universal reference and Perfect forwarding
==========================================
Universal refrence is used in case of type deduction.
class X;
void f(X&& param);
X&& var1 = X();
auto&& var2 = var1;
template <typename T>
void f(std::vector<T>&& param);
template <typename T>
void f(T&& param);
template <typename T>
void f(const T&& param);
X var;
f(var);
f(std::move(var));
std::vector<int> v;
f(v);
template <class T, class Allocator = allocator<T>>
class vector
{
public:
void push_back(T&& x);
};
template <class T, class Allocator = allocator<T>>
class vector
{
public:
template <class... Args>
void emplace_back(Args&&... args);
};
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());
factory<X>(42);
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&&);
- 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>
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>
decltype(auto) std::move(T&& a)
{
using ReturnType = remove_reference_t<T>&&;
return static_cast<ReturnType>(a);
}
std::move(x) <===> static_cast<X&&>(x)