¿Cuáles son las reglas para la generación automática de operaciones de movimiento?

En C ++ 98, el comstackdor de C ++ podría generar automáticamente el constructor de copias y el operador de asignación de copias a través de una copia de miembros, por ejemplo

struct X { std::string s; std::vector v; int n; }; 

El comstackdor genera automáticamente el constructor de copias y el operador de asignación de copias para X , utilizando una copia para miembros.

Pero, ¿cómo cambian las cosas en C ++ 11 con la semántica de movimiento?

¿El generador de movimiento y el operador de asignación de movimiento se generan automáticamente , como los constructores de copia y los operadores de asignación de copias?

¿Hay casos en que las operaciones de movimiento no se generan automáticamente?

Del estándar Ch. 12 – Funciones especiales de miembro

Par 12.8 Copiar y mover objetos de clase (énfasis mío)

9. Si la definición de una clase X no declara explícitamente un constructor de movimiento, se declarará implícitamente como predeterminado si y solo si

– X no tiene un constructor de copia declarado por el usuario,

– X no tiene un operador de asignación de copia declarado por el usuario,

– X no tiene un operador de asignación de movimiento declarado por el usuario, y

– X no tiene un destructor declarado por el usuario.

[Nota: cuando el constructor de movimientos no se declara implícitamente o no se proporciona explícitamente, las expresiones que de otro modo habrían invocado al constructor de movimientos pueden invocar en su lugar un constructor de copias. -Finalizar nota]

Luego 11 explica las reglas para eliminar el constructor de movimiento predeterminado

11. Un constructor de copia / movimiento implícitamente declarado es un miembro público en línea de su clase. Un constructor de copia / movimiento predeterminado para una clase X se define como eliminado (8.4.3) si X tiene:

– un miembro de variante con un constructor correspondiente no trivial y X es una clase de tipo union,

– un miembro de datos no estáticos del tipo de clase M (o matriz del mismo) que no se puede copiar / mover porque la resolución de sobrecarga (13.3), aplicada al constructor correspondiente de M, da como resultado una ambigüedad o una función que se elimina o inaccesible del constructor incumplido,

– una clase base directa o virtual B que no se puede copiar / mover porque la resolución de sobrecarga (13.3), como se aplica al constructor correspondiente de B, da como resultado una ambigüedad o una función que se elimina o inaccesible del constructor predeterminado,

– cualquier clase base directa o virtual o miembro de datos no estáticos de un tipo con un destructor que se elimine o inaccesible del constructor predeterminado, o

– para el constructor de copia, un miembro de datos no estáticos del tipo de referencia rvalue. La resolución de sobrecarga (13.3, 13.4) ignora un constructor de movimiento predeterminado que se define como eliminado.

[Nota: un constructor de movimiento eliminado interferiría de otra manera con la inicialización de un valor r que puede usar el constructor de copia en su lugar. -Finalizar nota]


En la complejidad de todo *

Las reglas pueden ser algo abrumadoras. Es bueno usar alguna técnica para eludir la complejidad . Los ejemplos son:

  1. Haga uso de la regla de cero para simplificar la escritura de la mayoría de sus clases.
  2. (En implícitamente eliminado) Establece explícitamente la función de miembro especial en cuestión; si se hubiera definido implícitamente como eliminado, el comstackdor se quejará.

* puntos hechos en los comentarios por mí mismo (1) y dyp (2)

Nikos Athanasiou dio una buena respuesta, pero quería agregar esta herramienta que creo que es muy útil.

Aquí hay una captura de pantalla de la presentación de Howard Hinnant “Todo lo que siempre quiso saber sobre Move Semantics (y algo más)” de la conferencia ACCU 2014, que creo que es un muy buen recordatorio de las reglas de generación automática de miembros especiales:

enter image description here

Aclaración del Sr. Hinnant de los comentarios:

La diapositiva no lo dice, pero los cuadrados rojos indican un comportamiento obsoleto. Es decir, si no desea depender del comportamiento obsoleto, declare ambos miembros de su copia si declara su destructor, o uno de los miembros de la copia (básicamente sigue la “regla de 3” de C ++ 98/03).

Recomiendo leer las diapositivas para obtener la construcción progresiva de esta tabla y tener una explicación detallada de cómo y por qué tenemos esto ahora.

Se pueden encontrar otras presentaciones allí: http://accu.org/index.php/articles/1901