¿Qué puedo hacer con un objeto movido desde?

138

¿El estándar define con precisión lo que puedo hacer con un objeto una vez que se ha movido? Solía ​​pensar que todo lo que puedes hacer con un objeto movido es destruirlo, pero eso no sería suficiente.

Por ejemplo, tome la plantilla de función swapcomo se define en la biblioteca estándar:

template <typename T>
void swap(T& a, T& b)
{
    T c = std::move(a); // line 1
    a = std::move(b);   // line 2: assignment to moved-from object!
    b = std::move(c);   // line 3: assignment to moved-from object!
}

Obviamente, debe ser posible asignar a objetos movidos, de lo contrario las líneas 2 y 3 fallarían. Entonces, ¿qué más puedo hacer con los objetos movidos? ¿Dónde puedo encontrar exactamente estos detalles en el estándar?

(Por cierto, ¿por qué está en T c = std::move(a);lugar de T c(std::move(a));en la línea 1?)

flujo libre
fuente

Respuestas:

53

Los objetos movidos desde existen en un estado no especificado, pero válido. Eso sugiere que, si bien el objeto podría no ser capaz de hacer mucho más, todas sus funciones miembro aún deberían exhibir un comportamiento definido, incluido operator=, y todos sus miembros en un estado definido, y aún requiere destrucción. El estándar no proporciona definiciones específicas porque sería exclusivo de cada UDT, pero es posible que pueda encontrar especificaciones para los tipos estándar. Algunos como los contenedores son relativamente obvios: simplemente mueven su contenido y un contenedor vacío es un estado válido bien definido. Las primitivas no modifican el objeto movido desde.

Nota al margen: creo que es T c = std::move(a)así que si el constructor de movimientos (o el constructor de copias si no se proporciona ningún movimiento) es explícito, la función fallará.

Perrito
fuente
26
No todas sus funciones miembro exhibirán un comportamiento definido. Solo aquellos sin condiciones previas. Por ejemplo, es probable que no desee pop_backun traslado desde vector. Pero ciertamente puedes averiguar si es así empty().
Howard Hinnant
66
@Howard Hinnant: pop_backdesde un vacío vectortiene un comportamiento indefinido de todos modos, desde la memoria, así que estoy bastante seguro de que pop_backun vector movido que exhibe un comportamiento indefinido es consistente.
Cachorro
12
Estamos discutiendo objetos movidos. No se sabe que los objetos estén vacíos. Los objetos movidos desde tienen un estado no especificado (a menos que, por supuesto, se especifique lo contrario). [lib.types.movedfrom]
Howard Hinnant
55
@Howard Sin especificar, pero válido, por lo que pop_backaún se comporta como en cualquier vector válido (incluso puede ser un vector vacío).
Christian Rau
1
¿Qué significan no especificado y válido en este contexto?
Ankur S
114

17.6.5.15 [lib.types.movedfrom]

Los objetos de los tipos definidos en la biblioteca estándar de C ++ se pueden mover desde (12.8). Las operaciones de movimiento pueden especificarse explícitamente o generarse implícitamente. A menos que se especifique lo contrario, dichos objetos retirados se colocarán en un estado válido pero no especificado.

Cuando un objeto está en un estado no especificado, puede realizar cualquier operación en el objeto que no tenga condiciones previas. Si hay una operación con condiciones previas que desea realizar, no puede realizarla directamente porque no sabe si el estado no especificado del objeto satisface las condiciones previas.

Ejemplos de operaciones que generalmente no tienen condiciones previas:

  • destrucción
  • asignación
  • observadores const tales como get, empty,size

Ejemplos de operaciones que generalmente tienen condiciones previas:

  • desreferencia
  • pop_back

Esta respuesta ahora aparece en formato de video aquí: http://www.youtube.com/watch?v=vLinb2fgkHk&t=47m10s

Howard Hinnant
fuente
1
Pero podría simplemente verificar las condiciones previas al igual que con cualquier otro objeto, ¿verdad?
fredoverflow
66
@FredOverflow Siempre y cuando estos controles no tengan condiciones previas, por supuesto.
Christian Rau
1
@ Chris: ¿Pero cómo es eso diferente de un objeto normal, no movido?
fredoverflow
2
May-be debería ser una pregunta separada, pero ¿eso significa: si tengo una cadena con char* buffer;y int length;miembros, entonces mi constructor / asignación de movimiento debe intercambiar (o establecer) el valor de ambos? ¿O estaría bien si la longitud no se especificara (lo que significa eso emptyy sizedevolvería valores sin sentido)?
UncleBens
3
@ 6502: No tiene sentido. Una clase C ++ 03 no está "violando el estándar C ++ 0x" porque un movimiento movido si se genera violaría el estándar. Y el código C ++ 03 no movería esa clase, por lo que no hay razón para que se genere un movimiento.
MSalters