c++ c++11 perfect-forwarding

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:

  1. la diferencia entre std::move y std::forward , preferiblemente con algunos ejemplos de código?
  2. 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.