std::forward în C++

Transmite un argument mai departe păstrând felul lui: valoare temporară (mutabilă) sau variabilă cu nume.

Sintaxă
#include <utility>
template<class T>
T&& forward(std::remove_reference_t<T>& arg) noexcept;

Ce face std::forward

În C++ o valoare poate fi o variabilă cu nume (lvalue, care trebuie copiată) sau un temporar (rvalue, care poate fi mutat, adică i se pot „fura” resursele, fără copiere). std::move transformă orice în rvalue. std::forward este varianta condiționată.

Într-un șablon cu parametru T&& arg (numit „referință universală”), std::forward<T>(arg) dă mai departe argumentul exact cum a venit: dacă apelantul a dat un temporar, se mută; dacă a dat o variabilă, se copiază. Aceasta se numește „perfect forwarding”.

Așa funcționează std::make_unique, emplace_back și alte funcții care trimit argumentele unui constructor.

Parametri

Parametru Descriere
T Tipul dedus al parametrului de șablon (scris explicit: std::forward<T>).
arg Parametrul de transmis mai departe.

Ce returnează

Argumentul, ca rvalue dacă a fost primit ca rvalue, altfel ca lvalue.

Exemple

Exemplul 1: Ce vede funcția finală

#include <print>
#include <string>
#include <utility>

void primeste(const std::string&) { std::println("copie (lvalue)"); }
void primeste(std::string&&) { std::println("mutare (rvalue)"); }

template <class T>
void intermediar(T&& arg) {
    primeste(std::forward<T>(arg));
}

int main() {
    std::string nume = "Ana";
    intermediar(nume);                     // variabilă cu nume
    intermediar(std::string("Ion"));       // temporar
    intermediar(std::move(nume));          // marcat explicit pentru mutare
}

Rezultat

copie (lvalue)
mutare (rvalue)
mutare (rvalue)

Exemplul 2: O funcție de tip make

creeaza trimite argumentele constructorului fără copii inutile, ca std::make_unique.

#include <memory>
#include <print>
#include <string>
#include <utility>

struct Client {
    std::string nume;
    int varsta;
    Client(std::string n, int v) : nume(std::move(n)), varsta(v) {}
};

template <class T, class... Args>
std::unique_ptr<T> creeaza(Args&&... args) {
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

int main() {
    auto c = creeaza<Client>("Elena", 31);
    std::println("{}, {} ani", c->nume, c->varsta);
}

Rezultat

Elena, 31 ani

Greșeli frecvente

  • Folosești std::forward fără argument de șablon (std::forward(arg)). Nu compilează: tipul T trebuie scris explicit.
  • Transmiți același argument cu std::forward de două ori. După prima transmitere, obiectul poate fi deja mutat (gol).
  • Folosești std::forward într-o funcție care nu este șablon. Acolo nu există „referință universală”; pentru mutare folosește std::move.

Comenzi înrudite

  • std::move: Mută elementele unui interval la o destinație, fără să le copieze (versiunea din <algorithm>).
  • std::exchange: Pune o valoare nouă într-o variabilă și returnează valoarea veche.
  • std::make_unique: Creează un obiect pe heap și returnează un std::unique_ptr care îl deține.
  • template: Scrie o funcție sau o clasă o singură dată, pentru orice tip; compilatorul generează varianta potrivită pentru fiecare tip folosit.