0
0
CppHow-ToIntermediate · 4 min read

How to Use std::forward in C++: Simple Guide and Examples

Use std::forward in C++ to pass function arguments while preserving their original value category (lvalue or rvalue). It is mainly used in template functions to enable perfect forwarding, ensuring efficient and correct argument passing.
📐

Syntax

The syntax of std::forward is:

template<typename T>
T&& std::forward(std::remove_reference_t<T>& param) noexcept;

Here:

  • T is the template type parameter representing the argument type.
  • param is the function parameter passed as an lvalue reference to a type with references removed.
  • std::forward<T>(param) casts param back to its original value category (lvalue or rvalue).

This allows forwarding the argument exactly as it was received.

cpp
template<typename T>
T&& forward(std::remove_reference_t<T>& param) noexcept;

// Usage inside a template function:
// std::forward<T>(param) forwards param preserving lvalue/rvalue nature
💻

Example

This example shows how std::forward preserves the value category of arguments when forwarding them through a template function.

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 relay(T&& arg) {
    process(std::forward<T>(arg));
}

int main() {
    int a = 10;
    relay(a);          // passes lvalue
    relay(20);         // passes rvalue
    return 0;
}
Output
Lvalue processed: 10 Rvalue processed: 20
⚠️

Common Pitfalls

Common mistakes when using std::forward include:

  • Using std::forward with a non-template type, which can cause incorrect casts.
  • Forgetting to use the template parameter T inside std::forward<T>, which breaks perfect forwarding.
  • Using std::move instead of std::forward in forwarding functions, which always casts to rvalue and can cause unnecessary moves.
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';
}

// Wrong: std::forward used without template parameter
// template<typename T>
// void relay_wrong(T&& arg) {
//     process(std::forward(arg)); // Error: missing <T>
// }

// Wrong: using std::move instead of std::forward
template<typename T>
void relay_move(T&& arg) {
    process(std::move(arg)); // Always treats arg as rvalue
}

int main() {
    int a = 10;
    relay_move(a); // Calls rvalue version incorrectly
    return 0;
}
Output
Rvalue processed: 10
📊

Quick Reference

Tips for using std::forward:

  • Use std::forward<T>(arg) inside template functions with universal references (T&&).
  • It preserves whether the argument is an lvalue or rvalue.
  • Do not use std::forward with non-template or fixed types.
  • Use std::move only when you want to unconditionally cast to rvalue.

Key Takeaways

Use std::forward in template functions to perfectly forward arguments preserving lvalue/rvalue nature.
Always specify the template parameter T in std::forward(arg) to avoid incorrect casts.
Do not use std::forward with non-template types; it is designed for universal references.
Use std::move only when you want to force an rvalue cast, not for perfect forwarding.
Perfect forwarding improves efficiency by avoiding unnecessary copies or moves.