una referencia que programación programacion orientada objetos objeto métodos miembros herencia clase atributos c++ c++11 language-lawyer move-semantics move-constructor

c++ - referencia - ¿Por qué el movimiento de clase derivada es construible cuando la clase base no es?



public y private en c++ (2)

Considere el siguiente ejemplo:

#include <iostream> #include <string> #include <utility> template <typename Base> struct Foo : public Base { using Base::Base; }; struct Bar { Bar(const Bar&) { } Bar(Bar&&) = delete; }; int main() { std::cout << std::is_move_constructible<Bar>::value << std::endl; // NO std::cout << std::is_move_constructible<Foo<Bar>>::value << std::endl; // YES. Why?! }

¿Por qué el compilador genera un constructor de movimiento a pesar de que la clase base no es movible?

¿Está eso en el estándar o es un error del compilador? ¿Es posible "propagar perfectamente" la construcción del movimiento desde la base a la clase derivada?


Porque:

La resolución de sobrecarga ignora un constructor de movimiento predeterminado que se define como eliminado.

([class.copy] / 11)

El constructor de movimiento de Bar se elimina explícitamente , por lo que Bar no se puede mover. Pero el constructor de movimiento de Foo<Bar> se elimina implícitamente después de ser declarado implícitamente como predeterminado, debido al hecho de que el miembro Bar no se puede mover. Por lo tanto, Foo<Bar> puede moverse usando su constructor de copia.

Editar: También olvidé mencionar el hecho importante de que una declaración de constructor heredado, como el using Base::Base , no hereda los constructores predeterminados, de copia o de movimiento, por eso Foo<Bar> no tiene un constructor de movimiento eliminado explícitamente del Bar .


1. El comportamiento de std::is_move_constructible

Este es el comportamiento esperado de std::is_move_constructible :

Los tipos sin un constructor de movimiento, pero con un constructor de copia que acepta argumentos const T& , satisfacen std::is_move_constructible .

Lo que significa que con un constructor de copia aún es posible construir T partir de la referencia de valor T&& . Y Foo<Bar> tiene un constructor de copia declarado implícitamente .

2. El constructor de movimiento declarado implícitamente de Foo<Bar>

¿Por qué el compilador genera un constructor de movimiento a pesar de que la clase base no es construible para mover?

De hecho, el constructor de movimiento de Foo<Bar> se define como deleted , pero tenga en cuenta que la resolución de sobrecarga ignora el constructor de movimiento declarado implícitamente eliminado.

El constructor de movimiento implícitamente declarado o predeterminado para la clase T se define como eliminado en cualquiera de los siguientes es verdadero:

... T has direct or virtual base class that cannot be moved (has deleted, inaccessible, or ambiguous move constructors); ...

La resolución de sobrecarga ignora el constructor de movimiento declarado implícitamente eliminado (de lo contrario, evitaría que la inicialización de copia rvalue).

3. El comportamiento diferente entre Bar y Foo<Bar>

Tenga en cuenta que el constructor de movimiento de Bar se declara como deleted explícitamente, y el constructor de movimiento de Foo<Bar> se declara implícitamente y se define como deleted . El punto es que la resolución de sobrecarga ignora el constructor de movimiento declarado implícitamente eliminado , lo que hace posible mover la construcción Foo<Bar> con su constructor de copia. Pero el constructor de movimiento eliminado explícitamente participará en la resolución de sobrecarga, lo que significa que al intentar mover la Bar constructor se seleccionará el constructor de movimiento eliminado, entonces el programa está mal formado.

Es por eso que Foo<Bar> es un movimiento construible pero Bar no.

El estándar tiene una declaración explícita sobre esto. $ 12.8 / 11 Copiar y mover objetos de clase [class.copy]

La resolución de sobrecarga ignora un constructor de movimiento predeterminado que se define como eliminado ([over.match], [over.over]). [Nota: un constructor de movimiento eliminado interferiría con la inicialización de un valor r que puede usar el constructor de copia. - nota final]