0
0
CppConceptIntermediate · 3 min read

What is Perfect Forwarding in C++: Explanation and Example

Perfect forwarding in C++ is a technique that allows a function template to pass its arguments to another function exactly as they were received, preserving their value category (lvalue or rvalue). It uses std::forward with universal references to achieve this, enabling efficient and correct argument passing.
⚙️

How It Works

Perfect forwarding works by capturing the exact type and value category (whether an argument is an lvalue or rvalue) of a function's parameters and then passing them on to another function without changing them. Imagine you are handing a package to a friend exactly as you received it, without opening or changing it. This way, the receiving function gets the argument in the same form it was originally given.

In C++, this is done using a special kind of reference called a universal (or forwarding) reference, which can bind to both lvalues and rvalues. The std::forward function then forwards the argument with its original type and value category intact. This avoids unnecessary copying or moving and ensures the called function behaves correctly depending on how the argument was passed.

💻

Example

This example shows a template function that perfectly forwards its argument to another function, preserving whether the argument is an lvalue or rvalue.

cpp
#include <iostream>
#include <utility>

void process(int& x) {
    std::cout << "Lvalue processed: " << x << "\n";
}

void process(int&& x) {
    std::cout << "Rvalue processed: " << x << "\n";
}

template <typename T>
void forwardToProcess(T&& arg) {
    process(std::forward<T>(arg));
}

int main() {
    int a = 10;
    forwardToProcess(a);        // Pass lvalue
    forwardToProcess(20);       // Pass rvalue
    return 0;
}
Output
Lvalue processed: 10 Rvalue processed: 20
🎯

When to Use

Use perfect forwarding when writing template functions that forward arguments to other functions, especially in generic code like wrappers, factories, or forwarding constructors. It helps keep your code efficient by avoiding unnecessary copies and ensures the correct function overloads are called based on the argument type.

For example, in a factory function that creates objects, perfect forwarding lets you pass constructor arguments exactly as received, whether they are temporary objects or named variables. This makes your code flexible and fast.

Key Points

  • Perfect forwarding preserves the original argument type and value category.
  • It uses universal references and std::forward to achieve this.
  • It avoids unnecessary copies and moves, improving performance.
  • Commonly used in template functions that forward arguments to other functions.

Key Takeaways

Perfect forwarding preserves whether arguments are lvalues or rvalues when passing them on.
Use universal references and std::forward to implement perfect forwarding.
It improves efficiency by avoiding unnecessary copying or moving of arguments.
Perfect forwarding is essential in generic code like wrappers and factory functions.