¿Qué es std :: move () y cuándo se debe usar?

  1. ¿Qué es?
  2. ¿Qué hace?
  3. ¿Cuándo debería usarse?

Se aprecian buenos enlaces.

http://en.wikipedia.org/wiki/C%2B%2B11#Rvalue_references_and_move_constructors
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html#Move_Semantics

  1. En C ++ 11, además de copiar constructores, los objetos pueden tener constructores de movimiento.
    (Y además de copiar los operadores de asignación, tienen operadores de asignación de movimiento).
  2. El constructor de movimiento se usa en lugar del constructor de copia, si el objeto tiene el tipo “referencia-valor-r” ( Type && ).
  3. std::move() es un elenco que produce una referencia rvalue a un objeto, para permitir moverse desde él.

Es una nueva forma de C ++ para evitar copias. Por ejemplo, al usar un constructor de movimiento, un std::vector podría simplemente copiar su puntero interno a los datos del nuevo objeto, dejando el objeto movido en un estado incorrecto, evitando copiar todos los datos. Esto sería C ++ – válido.

Intenta buscar en Google semántica de movimiento, valor r, reenvío perfecto.

Puedes usar mover cuando necesites “transferir” el contenido de un objeto a otro lugar, sin hacer una copia (por ejemplo, el contenido no está duplicado, es por eso que podría ser usado en algunos objetos no copiables, como un único_punto). También es posible que un objeto tome el contenido de un objeto temporal sin hacer una copia (y ahorre mucho tiempo), con std :: move.

Este enlace realmente me ayudó:

http://thbecker.net/articles/rvalue_references/section_01.html

Lo siento si mi respuesta llega demasiado tarde, pero también estaba buscando un buen enlace para el estándar :: movimiento, y encontré los enlaces de arriba un poco “austeros”.

Esto pone énfasis en la referencia del valor r, en cuyo contexto debe usarlos, y creo que es más detallado, por eso quería compartir este enlace aquí.

1. “¿Qué es?”

Mientras std::move() “parece” una función, diría que no es realmente una función . Es una especie de convertidor entre las formas en que el comstackdor considera el valor de una expresión.

2. “¿Qué hace?”

Lo primero a tener en cuenta es que std::move() realidad no mueve nada .

Si alguna vez ha visto la serie de animación Bleach , hace el equivalente al ablandamiento Reishi de Quincy Seele Schneider .

En serio, sin embargo, convierte una expresión de ser un valor lval o puro (como una variable que puede estar usando durante mucho tiempo, o un temporal que está pasando por un tiempo, respectivamente) a ser un valor x . Un xvalue le dice al comstackdor:

Puedes saquearme, mover todo lo que tengo y usarlo en otro lado (ya que de todos modos me van a destruir pronto) “.

en otras palabras, cuando usas std::move(x) , estás permitiendo que el comstackdor canibalice x . Por lo tanto, si x tiene, por ejemplo, su propio búfer en la memoria, después de std::move() el comstackdor puede tener otro objeto propio en su lugar.

3. “¿Cuándo debería usarse?”

Otra forma de hacer esta pregunta es “¿Para qué usaría / canibalizaré los recursos de un objeto?” Bueno, si está escribiendo un código de aplicación, probablemente no esté jugando mucho con los objetos temporales creados por el comstackdor. Así que principalmente harías esto en lugares como constructores, métodos de operador, funciones tipo algoritmo STL, etc. donde los objetos se crean y se destruyen automágicamente. Por supuesto, eso es solo una regla general.

Un uso típico es “mover” recursos de un objeto a otro en lugar de copiar. @Guillaume enlaces a esta página que tiene un breve ejemplo directo: el intercambio de dos objetos con menos copia.

 template  swap(T& a, T& b) { T tmp(a); // we now have two copies of a a = b; // we now have two copies of b (+ discarded a copy of a) b = tmp; // we now have two copies of tmp (+ discarded a copy of b) } 

Usar movimiento le permite intercambiar los recursos en lugar de copiarlos:

 template  swap(T& a, T& b) { T tmp(std::move(a)); a = std::move(b); b = std::move(tmp); } 

Piense en lo que sucede cuando T es, por ejemplo, vector de tamaño n. En la primera versión lee y escribe 3 * n elementos, en la segunda versión básicamente lee y escribe solo los 3 punteros a los búferes de los vectores. Por supuesto, la clase T necesita saber cómo hacer el movimiento; deberías tener un operador de asignación de movimiento y un constructor de movimiento para la clase T para que esto funcione.

std :: move itself realmente no hace mucho. Pensé que llamaba al constructor movido para un objeto, pero realmente solo realiza un molde de tipo (fundiendo una variable lvalue a un valor r para que dicha variable se pueda pasar como un argumento a un constructor de movimiento o un operador de asignación).

Entonces std :: move solo se usa como precursor del uso de la semántica de movimientos. La semántica de movimiento es esencialmente una forma eficiente de tratar con objetos temporales.

Considere el Objeto A = B + C + D + E + F;

Este es un código atractivo, pero E + F produce un objeto temporal. Entonces D + temp produce otro objeto temporal y así sucesivamente. En cada operador “+” normal de una clase, ocurren copias profundas.

Por ejemplo

 Object Object::operator+ (const Object& rhs) { Object temp (*this); // logic for adding return temp; } 

La creación del objeto temporal en esta función es inútil: estos objetos temporales se eliminarán al final de la línea, ya que saldrán del scope.

Podemos usar la semántica de movimiento para “saquear” los objetos temporales y hacer algo como

  Object& Object::operator+ (Object&& rhs) { // logic to modify rhs directly return rhs; } 

Esto evita que se hagan copias profundas innecesarias. Con referencia al ejemplo, la única parte donde se produce una copia profunda ahora es E + F. El rest usa semántica de movimiento. El constructor de movimientos o el operador de asignación también deben implementarse para asignar el resultado a A.

P: ¿Qué es std::move ?

A: std::move() es una función de la biblioteca estándar de C ++ para convertir a una referencia rvalue.

Simplísticamente std::move(t) es equivalente a:

 static_cast(t); 

Un rvalue es un temporal que no persiste más allá de la expresión que lo define, como un resultado de función intermedia que nunca se almacena en una variable.

 int a = 3; // 3 is a rvalue, does not exist after expression is evaluated int b = a; // a is a lvalue, keeps existing after expression is evaluated 

Se proporciona una implementación para std :: move () en N2027: “Breve introducción a las referencias de Rvalue” de la siguiente manera:

 template  typename remove_reference::type&& std::move(T&& a) { return a; } 

Como puede ver, std::move devuelve T&& sin importar si se lo llama con un valor ( T ), un tipo de referencia ( T& ) o una referencia de valor r ( T&& ).

P: ¿Qué hace?

R: Como un elenco, no hace nada durante el tiempo de ejecución. Solo es relevante en el momento de la comstackción decirle al comstackdor que desea continuar considerando la referencia como un valor r.

 foo(3 * 5); // obviously, you are calling foo with a temporary (rvalue) int a = 3 * 5; foo(a); // how to tell the compiler to treat `a` as an rvalue? foo(std::move(a)); // will call `foo(int&& a)` rather than `foo(int a)` or `foo(int& a)` 

Lo que no hace:

  • Haz una copia del argumento
  • Llamar al constructor de copia
  • Cambiar el objeto argumento

P: ¿Cuándo debería usarse?

R: Debería usar std::move si desea llamar funciones que soporten semántica de movimiento con un argumento que no sea un valor r (expresión temporal).

Esto me plantea las siguientes preguntas de seguimiento:

  • ¿Qué son semánticas de movimiento? La semántica de movimiento, en contraste con la semántica de copia, es una técnica de progtwigción en la que los miembros de un objeto se inicializan “asumiendo el control” en lugar de copiar los miembros de otro objeto. Tal ‘hacerse cargo’ solo tiene sentido con los punteros y los identificadores de recursos, que pueden transferirse de forma económica copiando el puntero o el identificador entero en lugar de los datos subyacentes.

  • ¿Qué tipo de clases y objetos soportan la semántica de movimiento? Depende de usted, como desarrollador, implementar la semántica de movimientos en sus propias clases si estos se beneficiarían de la transferencia de sus miembros en lugar de copiarlos. Una vez que implemente la semántica de movimientos, se beneficiará directamente del trabajo de muchos progtwigdores de bibliotecas que han agregado soporte para manejar clases con semántica de movimientos de manera eficiente.

  • ¿Por qué el comstackdor no puede resolverlo por sí mismo? El comstackdor no puede simplemente llamar a otra sobrecarga de una función a menos que usted lo diga. Debe ayudar al comstackdor a elegir si se debe invocar la versión regular o mover de la función.

  • ¿En qué situaciones quisiera decirle al comstackdor que debería tratar una variable como un valor r? Es muy probable que esto ocurra en las funciones de plantilla o biblioteca, donde usted sabe que un resultado intermedio podría salvarse.

What is it? y What does it do? ha sido explicado arriba.

Daré un ejemplo de when it should be used.

Por ejemplo, tenemos una clase con muchos recursos, como una gran matriz.

 class ResHeavy{ // ResHeavy means heavy resource public: ResHeavy(int len=10):_upInt(new int[len]),_len(len){ cout<<"default ctor"< _upInt; // heavy array resource int _len; // length of int array }; 

Código de prueba:

 void test_std_move2(){ ResHeavy rh; // only one int[] // operator rh // after some operator of rh, it becomes no-use // transform it to other object ResHeavy rh2 = std::move(rh); // rh becomes invalid // show rh, rh2 it valid if(rh.is_up_valid()) cout<<"rh valid"< 

salida como abajo:

 default ctor move ctor rh invalid rh2 valid copy ctor rh3 valid 

Podemos ver que std::move con move constructor hace que el recurso se transforme fácilmente.