c++ - ¿Cuál es la diferencia entre std:: move y std:: forward
c++11 perfect-forwarding (3)
Tanto std::forward
como std::move
no son más que moldes.
X x;
std::move(x);
Lo anterior arroja la expresión lvalue x
del tipo X a una expresión rvalue de tipo X (un xvalor para ser exacto). move
también puede aceptar un valor r:
std::move(make_X());
y en este caso es una función de identidad: toma un valor r de tipo X y devuelve un valor r de tipo X.
Con std::forward
puede seleccionar el destino hasta cierto punto:
X x;
std::forward<Y>(x);
Lanza la expresión lvalue x
del tipo X a una expresión de tipo Y. Hay restricciones sobre lo que Y puede ser.
Y puede ser una Base accesible de X, o una referencia a una Base de X. Y puede ser X, o una referencia a X. Uno no puede rechazar cv-qualifiers con forward
, pero uno puede agregar cv-qualifiers. Y no puede ser un tipo que sea meramente convertible desde X, excepto a través de una conversión base accesible.
Si Y es una referencia lvalue, el resultado será una expresión lvalue. Si Y no es una referencia lvalue, el resultado será una expresión rvalue (xvalue para ser preciso).
forward
puede tomar un argumento rvalue solo si Y no es una referencia lvalue. Es decir, no puedes convertir un valor r en lvalue. Esto es por razones de seguridad, ya que hacerlo generalmente lleva a referencias pendientes. Pero lanzar un rvalue a rvalue está bien y permitido.
Si intenta especificar Y a algo que no está permitido, el error se detectará en tiempo de compilación, no de tiempo de ejecución.
Vi esto aquí: Move Constructor llamando a la clase base Move Constructor
Podría alguien explicar:
- la diferencia entre
std::move
ystd::forward
, preferiblemente con algunos ejemplos de código? - Cómo pensarlo fácilmente, y cuándo usarlo
std::forward
se utiliza para reenviar un parámetro exactamente de la manera en que se pasó a una función. Como se muestra aquí:
Cuándo usar std :: forward para reenviar argumentos?
El uso de std::move
ofrece un objeto como un valor r, para posiblemente coincidir con un constructor de movimiento o una función que acepte valores de r. Lo hace para std::move(x)
incluso si x
no es un valor por sí mismo.
std::move
toma un objeto y le permite tratarlo como temporal (un valor r). Aunque no es un requisito semántico, por lo general, una función que acepte una referencia a un valor r lo invalidará. Cuando ve std::move
, indica que el valor del objeto no se debe usar después, pero aún puede asignar un nuevo valor y continuar usándolo.
std::forward
tiene un único caso de uso: para convertir un parámetro de función con plantilla (dentro de la función) en la categoría de valor (lvalue o rvalue) que la persona que llama utilizó para pasarlo. Esto permite que los argumentos rvalue pasen como valores r, y lvalues para pasarlos como lvalues, un esquema llamado "reenvío perfecto".
Para illustrate :
void overloaded( int const &arg ) { std::cout << "by lvalue/n"; }
void overloaded( int && arg ) { std::cout << "by rvalue/n"; }
template< typename t >
/* "t &&" with "t" being template param is special, and adjusts "t" to be
(for example) "int &" or non-ref "int" so std::forward knows what to do. */
void forwarding( t && arg ) {
std::cout << "via std::forward: ";
overloaded( std::forward< t >( arg ) );
std::cout << "via std::move: ";
overloaded( std::move( arg ) ); // conceptually this would invalidate arg
std::cout << "by simple passing: ";
overloaded( arg );
}
int main() {
std::cout << "initial caller passes rvalue:/n";
forwarding( 5 );
std::cout << "initial caller passes lvalue:/n";
int x = 5;
forwarding( x );
}
Como Howard menciona, también hay similitudes ya que ambas funciones simplemente se convierten en tipo de referencia. Pero fuera de estos casos de uso específicos (que cubren el 99,9% de la utilidad de los moldes de referencia rvalue), debe usar static_cast
directamente y escribir una buena explicación de lo que está haciendo.