C++ de los fundamentos al dominio — Lección 19
Conservar categorías de valor con perfect forwarding
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.