c++ - puede - tipos de conversiones matematicas




Entender la inicialización de la copia y las conversiones implícitas. (2)

Bastante seguro de que el compilador solo puede considerar una sola conversión implícita. En el primer caso, solo se requiere la conversión de std::unique_ptr<derived>&& a std::unique_ptr<base>&& , en el segundo caso, el puntero base también debería convertirse a test (para que funcione el constructor de movimientos predeterminado). Así, por ejemplo, convertir el puntero derivado en base: std::unique_ptr<base> bd = std::move(pd) y luego mover la asignación también funcionaría.

Tengo problemas para entender por qué no se compila la siguiente inicialización de copia:

#include <memory>

struct base{};
struct derived : base{};

struct test
{
    test(std::unique_ptr<base>){}
};

int main()
{
    auto pd = std::make_unique<derived>();
    //test t(std::move(pd)); // this works;
    test t = std::move(pd); // this doesn't
}

Un unique_ptr<derived> se puede mover a unique_ptr<base> , entonces, ¿por qué la segunda declaración funciona pero la última no? ¿No se consideran los constructores no explícitos al realizar una inicialización de copia?

El error de gcc-8.2.0 es:

conversion from 'std::remove_reference<std::unique_ptr<derived, std::default_delete<derived> >&>::type' 
{aka 'std::unique_ptr<derived, std::default_delete<derived> >'} to non-scalar type 'test' requested

y desde Clang-7.0.0 es

candidate constructor not viable: no known conversion from 'unique_ptr<derived, default_delete<derived>>' 
to 'unique_ptr<base, default_delete<base>>' for 1st argument

El código en vivo está disponible here .


La semántica de los inicializadores se describe en [dcl.init] ¶17 . La elección de la inicialización directa frente a la inicialización de la copia nos lleva a una de dos viñetas diferentes:

Si el tipo de destino es un tipo de clase (posiblemente cv-calificado):

  • [...]

  • De lo contrario, si la inicialización es una inicialización directa, o si es una inicialización de copia donde la versión no calificada cv del tipo fuente es la misma clase que la clase de destino, o una clase derivada de la clase del destino, se consideran constructores. Los constructores correspondientes se enumeran ([over.match.ctor]), y el mejor se elige mediante resolución de sobrecarga. El constructor así seleccionado se llama para inicializar el objeto, con la expresión inicializadora o lista de expresiones como su argumento (s). Si no se aplica ningún constructor, o la resolución de sobrecarga es ambigua, la inicialización está mal formada.

  • De lo contrario (es decir, para los casos de inicialización de copia restantes), las secuencias de conversión definidas por el usuario que pueden convertirse del tipo de origen al tipo de destino o (cuando se usa una función de conversión) a una clase derivada del mismo se enumeran como se describe en [sobre .match.copy], y el mejor se elige mediante resolución de sobrecarga. Si la conversión no se puede realizar o es ambigua, la inicialización no se ha realizado correctamente. La función seleccionada se llama con la expresión inicializadora como su argumento; Si la función es un constructor, la llamada es un prvalor de la versión no calificada de CV del tipo de destino cuyo objeto de resultado es inicializado por el constructor. La llamada se utiliza para inicializar directamente, de acuerdo con las reglas anteriores, el objeto que es el destino de la inicialización de la copia.

En el caso de inicialización directa, ingresamos la primera viñeta entre comillas. Como se detalla allí, los constructores son considerados y enumerados directamente. La secuencia de conversión implícita que se requiere es, por lo tanto, solo para convertir unique_ptr<derived> en un unique_ptr<base> como un argumento de constructor.

En el caso de inicialización de copia, ya no estamos considerando directamente a los constructores, sino que intentamos ver qué secuencia de conversión implícita es posible. El único disponible es unique_ptr<derived> para un unique_ptr<base> para una test . Dado que una secuencia de conversión implícita puede contener solo una conversión definida por el usuario, esto no está permitido. Como tal, la inicialización está mal formada.

Se podría decir que el uso de la inicialización directa de "omite" una conversión implícita.





unique-ptr