← SEARCHABLE ARCHIVE
C / C++ · 2026-08-13

C++ de los fundamentos al dominio — Lección 19

Conservar categorías de valor con perfect forwarding

INTERACTIVE READING MODE →

Los artículos de este blog se han escrito con fines didácticos. Pueden contener errores; contrasta la información con el compilador y la biblioteca estándar de tu plataforma.

Incluso con move semantics, un wrapper genérico puede convertir accidentalmente todos los argumentos en lvalue. Perfect forwarding conserva la categoría de valor del llamador al invocar la siguiente función.

Una expresión con nombre es lvalue

void Store(const Texture& texture);
void Store(Texture&& texture);

Texture loaded = LoadTexture();
Store(loaded);              // lvalue
Store(std::move(loaded));   // rvalue
Store(LoadTexture());       // rvalue

Un lvalue identifica un objeto persistente. Un rvalue puede representar un temporal o un objeto ofrecido explícitamente para mover. Incluso una variable de tipo Texture&& es una expresión lvalue cuando se usa por su nombre.

Forwarding reference y colapso de referencias

template <typename T>
void Register(T&& value)
{
    Store(value);
}

Cuando T se deduce, este T&& es una forwarding reference. Un lvalue deduce una referencia lvalue; un rvalue conserva su referencia rvalue tras el colapso.

Dentro de la función, value tiene nombre, así que Store(value) siempre elige la sobrecarga lvalue.

Restaurar la categoría con std::forward

template <typename T>
void Register(T&& value)
{
    Store(std::forward<T>(value));
}

std::forward<T> reenvía un lvalue original como lvalue y un rvalue como rvalue. A diferencia de std::move, no convierte incondicionalmente.

template <typename T, typename... Args>
std::unique_ptr<T> MakeOwned(Args&&... args)
{
    return std::make_unique<T>(std::forward<Args>(args)...);
}

Las factories usan este patrón para transmitir argumentos de constructor sin inventar copias.

No toda API necesita forwarding references

void Player::SetName(std::string name)
{
    Name = std::move(name);
}

Un setter que guarda un string suele ser más simple si recibe por valor y mueve una vez. Las forwarding references aceptan tipos muy amplios y complican los diagnósticos. Úsalas en adapters genéricos transparentes; en límites de aplicación prefiere tipos concretos y propiedad explícita.