c++ c++11 unique-ptr c++14

c++ - Conversión de std:: unique_ptr<Derived> a std:: unique_ptr<Base>



c++11 unique-ptr (2)

Digamos que tengo funciones de fábrica que tratan con clases base y derivadas:

#include <memory> using namespace std; struct B { virtual ~B() {} }; struct D : B {}; unique_ptr<B> MakeB() { auto b = unique_ptr<B>( new B() ); return b; // Ok! } unique_ptr<B> MakeD() { auto d = unique_ptr<D>( new D() ); return d; // Doh! }

En la última línea anterior, necesito move(d) para que funcione, de lo contrario, std::unique_ptr<D> "Error: conversión no válida de std::unique_ptr<D> a std::unique_ptr<D>&& ." Mi intuición decía que, en este contexto, el compilador debería saber que implícitamente podría hacer d un valor r y moverlo al puntero base, pero no es así.

¿Es esto una no conformidad en mis compiladores (gcc 4.8.1 y VS2012)? ¿El diseño previsto de unique_ptr ? Un defecto en el estándar?


Con la adición de las llamadas std::move en las declaraciones de return , este código funciona para mí en Visual Studio 2013:

#include <memory> using namespace std; struct B { virtual ~B() {} }; struct D : B {}; unique_ptr<B> MakeB() { auto b = unique_ptr<B>( new B() ); return std::move( b ); // *** std::move() added here! *** } unique_ptr<B> MakeD() { auto d = unique_ptr<D>( new D() ); return std::move( d ); // *** std::move() added here! *** }

Lo siguiente también funciona

unique_ptr<B> MakeB() { return unique_ptr<B>( new B() ); // *** Returning a temporary! *** } unique_ptr<B> MakeD() { return unique_ptr<D>( new D() ); // *** Returning a temporary! *** }

Por supuesto, si devuelve std::unique_ptr<B> todos modos, ¿por qué no meter la instancia D en std::unique_ptr<B> en primer lugar? ¡Entonces no hay conversión necesaria en absoluto!


El comportamiento del compilador es correcto. Solo hay un movimiento implícito cuando los tipos son los mismos, porque el movimiento implícito se especifica en términos de que el compilador no puede realizar la elisión de copia en los casos en que está permitido (ver 12.8 / 31 y 12.8 / 32).

12.8 / 31 (elisión de copia):

en una instrucción de retorno en una función con un tipo de retorno de clase, cuando la expresión es el nombre de un objeto automático no volátil (que no sea una función o parámetro catch-clause) con el mismo tipo cv-no calificado que el tipo de retorno de función . ..

12.8 / 32 (movimiento implícito):

Cuando se cumplen los criterios para la elisión de una operación de copia, [...] la resolución de sobrecarga para seleccionar el constructor para la copia se realiza primero como si el objeto fuera designado por un valor r .