Perfect Forwarding
Passing arguments through a wrapper without losing their value category
const or not. The three pieces that make it work are a forwarding reference (T&& on a deduced type), the reference-collapsing rules the compiler applies, and std::forward, a conditional cast that restores the original value category at the point of the call.
- explain the difference between an rvalue reference and a forwarding reference, even though both are spelled
T&&, - apply the four reference-collapsing rules by hand,
- use
std::forward<T>correctly (and say whystd::movewould be wrong here), - write variadic factory functions and forwarding constructors that avoid the greedy-constructor trap.
1. The problem: a wrapper that keeps the value category
You already know value categories from the move-semantics course: an lvalue names a persistent object (you can take its address), and an rvalue is a temporary that can be moved from. Move semantics only pays off when a temporary is actually detected as an rvalue and its resources are stolen instead of copied.
Now imagine a thin wrapper - a logging layer, a factory, a container's emplace - that receives an argument and immediately hands it to some target function sink. We want the wrapper to be invisible: if the caller passed a temporary, sink should see a temporary (and move); if the caller passed a named object, sink should see an lvalue (and copy). Naive attempts all fail:
void wrap(T v)- takes by value, forcing an extra copy or move of every argument.void wrap(const T& v)- always an lvalue reference to const;sinkcan never move, even from a genuine temporary.void wrap(T& v)- rejects rvalues and const objects entirely.
Writing a separate overload for every combination of const/non-const and lvalue/rvalue does not scale: a function of n parameters would need 2^n overloads. Perfect forwarding solves the whole problem with a single template.