Skip to content

Perfect Forwarding

Perfect forwarding allows you to pass arguments through a function while preserving their original value category (l-value/r-value). This is essential for writing generic, efficient template code.

The Problem: Value Category Loss

cpp
void process(int& value);
void process(int&& value);

template<typename T>
void wrapper(T param) {
    process(param);  // Calls the l-value overload!
}

wrapper(42);        // 42 is rvalue, but wrapper will call the l-value overload!

The Solution: Universal References

cpp
template<typename T>
void wrapper(T&& param) {  // Universal reference
    process(std::forward<T>(param));  // Preserves value category
}

wrapper(42);        // Calls process with rvalue
int x = 42;
wrapper(x);         // Calls process with lvalue

Universal references are formed when:

  • T&& is used in a template parameter
  • Type deduction occurs
cpp
template<typename T>
void f(T&& param);  // Universal reference

template<typename T>
class Widget {
    void f(T&& param);  // NOT universal reference (no deduction)
};

std::forward

std::forward is used to forward the original value category of the argument to the function. How it works:

  • If T is lvalue reference → static_cast<T&> (no change)
  • If T is not reference → static_cast<T&&> (to rvalue)

Questions

Q: What is the main purpose of perfect forwarding?

Perfect forwarding preserves the original value category (l-value/r-value) of arguments when passing them through template functions. This allows the called function to receive arguments with the same value category as they were originally passed.

Q: What is a universal reference?

A universal reference is formed when T&& is used in a template parameter and type deduction occurs. It can bind to both l-values and r-values, making it 'universal'.

Q: What does std::forward do?

std::forward conditionally casts to r-value reference only if the original argument was an r-value. If the original was an l-value, it remains an l-value.

Q: What happens when you pass an r-value to a template function with T&& parameter?

When passing an r-value to T&&, T is deduced as the type (not a reference), and param becomes an r-value reference to that type.

Q: What happens when you pass an l-value to a template function with T&& parameter?

When passing an l-value to T&&, T is deduced as l-value reference, and due to reference collapse, param becomes an l-value reference.

Q: Why is perfect forwarding important for generic code?

A: It reduces compilation time

Perfect forwarding allows generic functions to pass arguments to other functions while preserving their original value category, enabling proper overload resolution and efficient move semantics.

Q: What is the relationship between perfect forwarding and move semantics?

Perfect forwarding enables efficient move semantics in generic code by preserving r-value references, allowing move constructors and move assignment operators to be called when appropriate.