2011-07-25 15 views
9

Duplicar posible:
Can someone please explain move semantics to me?Semántica de movimiento: ¿de qué se trata?

alguien podría apuntar a una buena fuente o explicar aquí lo son la semántica movimiento?

+3

@Armen: ¿Qué tiene que ver Google con algo? La última vez que revisé, SO era un mejor recurso para programar preguntas que Google. Creo que incluso el * objetivo * de SO es un mejor recurso para las preguntas de programación. Entonces, ¿por qué debería alguien perder el tiempo en las búsquedas de Google que, el 90% del tiempo, conducen directamente a una pregunta de SO * de todos modos *? – jalf

+3

@jalf: 'Señalarme una buena fuente para explicar qué es la semántica de movimientos'. Creo que uno debe buscar primero en Google, luego ver los enlaces, y cuando no esté satisfecho, señale en la pregunta que uno leyó esto, esto y esto, y uno no estaba satisfecho y por qué. Pero esa es solo mi opinión personal –

+0

Aquí está la breve introducción original: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html –

Respuesta

33

Olvídate de C++ 0x por el momento. La semántica de movimiento es independiente del lenguaje: C++ 0x simplemente proporciona una forma estándar de realizar operaciones con semántica de movimiento.

Definición

semántica Mover definir el comportamiento de ciertas operaciones. La mayoría de las veces se contrastan con semántica de copia, por lo que sería útil definirlas primero.

Asignación con la semántica de copia tiene el siguiente comportamiento:

// Copy semantics 
assert(b == c); 
a = b; 
assert(a == b && b == c); 

decir a termina igual a b, y nos vamos b sin cambios.

Asignación con semántica movimiento tiene condiciones de correos más débiles:

// Move semantics 
assert(b == c); 
move(a, b); // not C++0x 
assert(a == c); 

nota de que ya no hay ninguna garantía de que es b se mantiene sin cambios después de la asignación con la semántica de movimiento. Esta es la diferencia crucial.

Usos

Uno de los beneficios de la semántica movimiento es que permite optimizaciones en ciertas situaciones. Consideremos el siguiente tipo de valor regular:

struct A { T* x; }; 

Supongamos también que se definen dos objetos de tipo A a ser igual si y sólo si su punto x miembro para valores iguales.

bool operator==(const A& lhs, const A& rhs) { return *lhs.x == *rhs.x; } 

Finalmente suponer que definir un objeto A tener la propiedad exclusiva sobre el pointee de su miembro de x.

A::~A() { delete x; } 
A::A(const A& rhs) : x(new T(rhs.x)) {} 
A& A::operator=(const A& rhs) { if (this != &rhs) *x = *rhs.x; } 

Ahora supongamos que queremos definir una función para intercambiar dos A objetos.

Podríamos hacerlo de la manera normal con la semántica de copia.

void swap(A& a, A& b) 
{ 
    A t = a; 
    a = b; 
    b = t; 
} 

Sin embargo, esto es innecesariamente ineficiente. ¿Que estamos haciendo?

  • Creamos una copia de a en t.
  • continuación copiamos b en a.
  • luego copiar t en b.
  • Por último, destruir t.

Si T objetos son caros de copiar, entonces esto es un desperdicio. Si le pedí que intercambiara dos archivos en su computadora, no crearía un tercer archivo y luego copiaría y pegaría el contenido del archivo antes de destruir su archivo temporal, ¿verdad? No, usted mover un archivo de distancia, mover el segundo en la primera posición, y finalmente mover el primer archivo en el segundo. No es necesario copiar datos.

En nuestro caso, es fácil moverse objetos de tipo A:

// Not C++0x 
void move(A& lhs, A& rhs) 
{ 
    lhs.x = rhs.x; 
    rhs.x = 0; 
} 

simplemente nos movemos rhs 's puntero en lhs y luego renunciamos rhs propiedad de ese puntero (estableciéndolo en null). Esto debería iluminar por qué la condición de post más débil de semántica de movimiento permite optimizaciones.

Con esta nueva operación de movimiento definido, podemos definir una permuta optimizado:

void swap(A& a, A& b) 
{ 
    A t; 
    move(t, a); 
    move(a, b); 
    move(b, t); 
} 

Otra de las ventajas de la semántica movimiento es que le permite moverse alrededor de los objetos que no pueden ser copiados. Un buen ejemplo de esto es std::auto_ptr.

C++ 0x

C++ 0x permite mover la semántica a través de su función de referencia rvalue. Específicamente, las operaciones de la clase:

a = b; 

tienen una semántica se mueven cuando b es una referencia rvalue (deletreado T&&), de lo contrario, tienen una semántica de copia. Puede forzar la semántica de movimiento mediante el uso de la función std::move (diferente de la move he definido antes) cuando b no es una referencia rvalue:

a = std::move(b); 

std::move es una función simple que proyecta esencialmente su argumento a una referencia de valor de lado derecho. Tenga en cuenta que los resultados de las expresiones (como una llamada a la función) son automáticamente referencias de valores, por lo que puede aprovechar la semántica de movimiento en esos casos sin cambiar su código.

Para definir optimizaciones movimiento, es necesario definir un constructor movimiento y mover operador de asignación:

T::T(T&&); 
T& operator=(T&&); 

A medida que estas operaciones tienen una semántica movimiento, usted es libre de modificar los argumentos pasados ​​(siempre y cuando salga el objeto en un estado destructible).

Conclusión

Eso es básicamente todo lo que hay que hacer. Tenga en cuenta que las referencias rvalue también se utilizan para permitir el reenvío perfecto en C++ 0x (debido a las interacciones del sistema de tipo específicamente diseñadas entre referencias rvalue y otros tipos), pero esto no está realmente relacionado con la semántica de movimiento, por lo que no he discutido aquí.

4

Básicamente, las referencias rvalue permitirá detectar cuando los objetos son temporales y que no tienen que preservar su estado interno. Esto permite un código mucho más eficiente donde C++ 03 solía tener que copiar todo el tiempo, en C++ 0x puede seguir reutilizando los mismos recursos. Además, las referencias rvalue permiten el reenvío perfecto.

Tenga una mirada en this answer.

+0

La semántica de movimiento y las referencias de valores r son independientes (pero conceptos relacionados. La relación es que la asignación a una referencia rvalue garantiza la validez de la semántica de movimiento. La semántica de movimiento existe sin referencias rvalue, y las referencias rvalue pueden existir sin operaciones con semántica de movimiento. Simplemente funcionan bien juntos (como inmutabilidad y concurrencia). –

2

He leído un montón de explicaciones de texto alrededor de un año y no comprendió todo lo relacionado con las referencias de valor R hasta que termine de ver esta excelente presentación de Scott Meyer: http://skillsmatter.com/podcast/home/move-semanticsperfect-forwarding-and-rvalue-references

Se explica de una manera que es gracioso y lo suficientemente lento como para entender cada cosa que sucede en los procesos.

Lo sé, es 1h30 pero realmente, es la mejor explicación que he tenido en el último año.

Después de haber leído los artículos (como las otras respuestas), ver este video se fundió en mi mente de manera consistente y pocos días después de que pude explicarlo a algunos colegas y explicarles cómo usar std: : unique_ptr (como está relacionado, solo permite la semántica de movimiento, no la copia) porque requiere la comprensión de std :: move(), que requiere la comprensión de la semántica de movimiento.

2

me alegro de ver una pregunta así y estoy feliz de compartir mi punto. Creo que estás preguntando por una corrección de errores en la designación del lenguaje C++ en sí mismo, no solo en otra característica del lenguaje C++. El "error" ha estado allí durante decenas de años. Es decir, copia constructor.

Copiar constructores parece muy extraño si sabes en física que hay muchas cosas que no se pueden copiar como la energía y la masa. Eso es solo una broma, pero de hecho también en el mundo de la programación, los objetos como los descriptores de archivos exclusivos no se pueden copiar. Entonces los programadores y diseñadores de C++ inventaron algunos trucos para lidiar con eso. Hay 3 famosos: NRVO, boost::noncopyable y std::auto_ptr.

NRVO (Optimización de valor de devolución designada) es una técnica que permite que una función devuelva un objeto por valor sin llamar al constructor de copia. Pero el problema con NRVO es que aunque el constructor de copia no se llama realmente, todavía se necesita una declaración del constructor de copia public, lo que significa que los objetos de boost::noncopyable no son compatibles con NRVO.

std::auto_ptr es otra prueba para eludir el constructor de copia. Es posible que haya visto su "constructor de copia", implementado como

template <typename _T> 
auto_ptr(auto_ptr<_T>& source) 
{ 
    _ptr = source._ptr; // where _ptr is the pointer to the contained object 
    source._ptr = NULL; 
} 

Ésta no es una copia en absoluto, sino un "movimiento". Podría considerar este tipo de comportamiento como el prototipo de un movimiento semántico.

Pero std::auto_ptr también tiene su propio problema: no es compatible con contenedores STL. Por lo tanto, desafortunadamente, todo lo que no se puede copiar es doloroso.

Eso fue doloroso hasta que, la semántica del movimiento C++ 0x finalmente se publica e implementa por los fabricantes de compiladores.

De manera simple, podría pensar en semántica de movimiento como algo similar al comportamiento de "copia" de std::auto_ptr, pero con un soporte completo por las características del lenguaje para que funcione bien con contenedores y algoritmos.

Por cierto en C++ 0x el std::auto_ptr está en desuso y se recomienda una nueva plantilla tipo std::unique_ptr.

Mi historia terminará ahora. Por favor, consulte otras publicaciones si desea obtener más información al respecto, como la sintaxis extraña y el sistema rvalue.

Cuestiones relacionadas