Última actualización:2026-10-12
C++ std::move: principio y uso¶
std::move es una función muy importante: proporciona una manera de marcar un objeto lvalue como un tipo especial de objeto rvalue, el xvalue. Esta transformación existe para permitir aplicar la semántica de movimiento en lugar de la semántica de copia habitual.
- Por qué usar std::move
- Escenarios de uso reales
1.Por qué usar std::move¶
Normalmente, solo los objetos rvalue pueden ser objeto de una operación de movimiento, porque el constructor de movimiento toma como parámetro una referencia rvalue, y una referencia rvalue solo puede vincularse a un rvalue. Por eso, si quieres ejecutar una operación de movimiento sobre un objeto representado por una expresión lvalue, necesitas usar std::move para convertirlo en un xvalue (un rvalue especial).
Conviene usar std::move con precaución, porque la operación de movimiento deja el objeto en un estado que normalmente solo permite su destrucción. Por ello, lo mejor es mover un objeto únicamente en su último uso.
std::move no ejecuta realmente el movimiento: hay que dejar claro que std::move no realiza la operación de movimiento del objeto. Lo único que hace es convertir el objeto lvalue dado en una referencia rvalue. A continuación, una posible implementación de std::move:
template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
return static_cast<typename remove_reference<T>::type&&>(t);
}
2.Escenarios de uso reales¶
2.1 Construcción por movimiento en lugar de construcción por copia¶
En la función f1, el parámetro a es un lvalue con tipo referencia rvalue, y no un rvalue en sí, porque tiene nombre. Por tanto, para forzar la construcción por movimiento de Foo debemos convertirlo en un rvalue, y std::move hace exactamente eso a la perfección.
struct Foo {
...
Foo(const Foo&) {}
Foo(Foo&&) {}
};
void f1(Foo&& a) {
Foo b(a); // 调用拷贝构造函数,而非移动构造函数
}
void f2(Foo&& a) {
Foo b(std::move(a)); // right
}
2.2 Gestión de la propiedad¶
Supongamos el siguiente código:
El código anterior funciona correctamente, pero no se puede determinar quién es el propietario del puntero. ¿Quién debe eliminar el puntero? ¿En qué momento debe eliminarse? ¿Se eliminará cuando el contenedor se destruya?
En C++11, la instrucción std::unique_ptr<Foo> p(new Foo()); produce un error de compilación. El motivo es que se aplica la regla de propiedad exclusiva.
Sin embargo, puedes usar std::move para transferir la propiedad del puntero:
Ahora la propiedad del puntero se ha transferido al contenedor, y se eliminará cuando el contenedor llegue al final de su ciclo de vida. Aunque el objeto p sigue existiendo, cualquier intento de desreferenciarlo generará una excepción de puntero nulo.
De hecho, tras la operación de movimiento, el puntero interno que poseía p ya se ha establecido en nullptr.
En resumen, la utilidad de std::move reside en ofrecer un mecanismo capaz de implementar de forma segura la semántica de movimiento de los objetos, transformando su condición de lvalue en rvalue para que puedan ser utilizados por el constructor de movimiento o por el operador de asignación por movimiento. Usar std::move correctamente puede mejorar el rendimiento y garantizar una transferencia de propiedad correcta.
Entonces, la siguiente pregunta: ¿cuándo no conviene usar std::move?
Temas relacionados¶
- Panorama de las novedades de C++11 | C++那些事
- C++ std::bind frente a lambda | C++那些事
- Plantillas variádicas en C++ | C++那些事