c++ - ¿Es un constructor de movimiento `= predeterminado` equivalente a un constructor de movimiento de miembro?
c++11 default (4)
Es esto equivalente a esto
Sí. Pero es válido solo para las clases que no tienen miembros no móviles. Mira este ejemplo:
#include <iostream>
struct nonmovable
{
nonmovable() = default;
nonmovable(const nonmovable &) = default;
nonmovable( nonmovable &&) = delete;
};
struct movable
{
movable() = default;
movable(const movable &) { std::cerr << "copy" << std::endl; }
movable( movable &&) { std::cerr << "move" << std::endl; }
};
struct has_nonmovable
{
movable a;
nonmovable b;
has_nonmovable() = default;
has_nonmovable(const has_nonmovable &) = default;
has_nonmovable( has_nonmovable &&) = default;
};
int main()
{
has_nonmovable c;
has_nonmovable d(std::move(c)); // prints copy
}
Imprime:
copy
http://coliru.stacked-crooked.com/a/62c0a0aaec15b0eb
Entonces, si una clase tiene incluso un único miembro no movible, el constructor de movimientos predeterminado de forma predeterminada usará los constructores de copia para todos los miembros, incluso para los que tengan el constructor de movimientos.
Pero los miembros no están obligados a tener explícitamente definidas o explícitamente incumplidas con los constructores de movimientos. Si el constructor de movimientos no se declara en absoluto, el constructor de movimientos se usará para los miembros que tengan el constructor de movimientos y el constructor de copias se usará para los miembros que no definen el constructor de movimientos. Mira el ejemplo:
#include <iostream>
struct nonmovable
{
nonmovable() = default;
nonmovable(const nonmovable &) { std::cerr << "nonmovable::copy" << std::endl; }
//nonmovable( nonmovable &&) = delete;
};
struct movable
{
movable() = default;
movable(const movable &) { std::cerr << "movable::copy" << std::endl; }
movable( movable &&) { std::cerr << "movable::move" << std::endl; }
};
struct has_nonmovable
{
movable a;
nonmovable b;
has_nonmovable() = default;
has_nonmovable(const has_nonmovable &) = default;
has_nonmovable( has_nonmovable &&) = default;
};
int main()
{
has_nonmovable c;
has_nonmovable d(std::move(c));
}
Imprime:
movable::move
nonmovable::copy
http://coliru.stacked-crooked.com/a/fda51f29a4b92881
Hay clases que son movibles pero no copiables (por ejemplo, unique_ptr
). Hay clases que se pueden copiar y usan el constructor de copia para moverse. Pero no conozco ninguna razón para hacer una clase que se pueda copiar pero que haya eliminado explícitamente el constructor de movimientos.
Es esto
struct Example {
int a, b;
Example(int mA, int mB) : a{mA}, b{mB} { }
Example(const Example& mE) : a{mE.a}, b{mE.b} { }
Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
Example& operator=(const Example& mE) { a = mE.a; b = mE.b; return *this; }
Example& operator=(Example&& mE) { a = move(mE.a); b = move(mE.b); return *this; }
}
equivalente a esto
struct Example {
int a, b;
Example(int mA, int mB) : a{mA}, b{mB} { }
Example(const Example& mE) = default;
Example(Example&& mE) = default;
Example& operator=(const Example& mE) = default;
Example& operator=(Example&& mE) = default;
}
?
Sí, un constructor de movimientos predeterminado realizará un movimiento de miembro de su base y miembros, por lo que:
Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
es equivalente a:
Example(Example&& mE) = default;
podemos ver esto yendo al borrador de la sección estándar de C ++ 11 12.8
Copiando y moviendo objetos de clase, párrafo 13, que dice ( énfasis mío en el futuro ):
Un constructor de copia / movimiento que está predeterminado y no se define como eliminado se define implícitamente si se usa o no (3.2) o cuando está explícitamente predeterminado después de su primera declaración. [Nota: el constructor copiar / mover está implícitamente definido incluso si la implementación elida su odr-use (3.2, 12.2). -finalizar] [...]
y el párrafo 15 que dice:
El constructor de copiar / mover implícitamente definido para una clase X no sindicalizada realiza una copia / movimiento de sus bases y miembros para miembros . [Nota: los inicializadores de llaves o igual de los miembros de datos no estáticos se ignoran. Ver también el ejemplo en 12.6.2. -finalizar nota] El orden de inicialización es el mismo que el orden de inicialización de las bases y los miembros en un constructor definido por el usuario (véase 12.6.2). Sea x el parámetro del constructor o, para el constructor de movimiento, un valor x referente al parámetro. Cada miembro de datos base o no estático se copia / mueve de la manera adecuada a su tipo:
- si el miembro es una matriz, cada elemento se inicializa directamente con el subobjeto correspondiente de x;
- si un miembro m tiene un valor de referencia rvalue T &&, se inicializa directamente con static_cast (xm);
- de lo contrario, la base o miembro se inicializa directamente con la base o miembro correspondiente de x.
Los subobjetos de clase de base virtual se inicializarán una sola vez mediante el constructor de copia / movimiento definido implícitamente (consulte 12.6.2).
aparte casos muy patológicos ... SÍ.
Para ser más precisos, también debe considerar las eventuales bases que pueda tener el Example
, con las mismas reglas exactas. Primero las bases -en orden de declaración- luego los miembros, siempre en orden de declaración.
Sí, ambos son lo mismo.
Pero
struct Example {
int a, b;
Example(int mA, int mB) : a{mA}, b{mB} { }
Example(const Example& mE) = default;
Example(Example&& mE) = default;
Example& operator=(const Example& mE) = default;
Example& operator=(Example&& mE) = default;
}
Esta versión le permitirá saltear la definición del cuerpo.
Sin embargo, debe seguir algunas reglas cuando declara explicitly-defaulted-functions
:
8.4.2 Funciones predeterminadas por defecto [dcl.fct.def.default]
Una definición de función de la forma:
attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt = default ;
se llama definición explícitamente predeterminada . Una función que está explícitamente incumplida deberá
ser una función de miembro especial,
tienen el mismo tipo de función declarada (excepto posibles ref-calificadores diferentes y excepto que en el caso de un constructor de copia o un operador de asignación de copia, el tipo de parámetro puede ser "referencia a
T
no const", dondeT
es el nombre del clase de la función miembro) como si hubiera sido implícitamente declarada,no tiene argumentos predeterminados