Skip to content

Perfect Forwarding

Beginning of the lesson

Perfect Forwarding

Passing arguments through a wrapper without losing their value category

Big Idea: Perfect forwarding is the technique that lets a generic function template accept any argument and pass it onward to another function exactly as it was received - preserving whether it was an lvalue or an rvalue, and whether it was 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.
By the end you will be able to:
  • 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 why std::move would 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; sink can 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.

The goal, stated precisely: an argument that arrives as an lvalue leaves as an lvalue; an argument that arrives as an rvalue leaves as an rvalue - and constness is preserved on both.

Continue this lesson on YourAIZone

The full lesson has the complete explanation, worked examples, common mistakes, tests with instant checking and an AI tutor. Start right away as a guest, or create an account to keep your progress.

In the full lesson

  • The complete explanation
  • Worked examples
  • Common mistakes and how to avoid them
  • Test questions: 15
  • AI tutor that answers questions about the topic
  • Progress tracking for students, parents and teachers