Преобразование std:: unique_ptr <Derived> в std:: unique_ptr <Base>
Скажем, у меня есть функции factory, относящиеся к базовому и производному классам:
#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!
}
В последней строке выше мне нужно move(d)
, чтобы заставить ее работать, иначе я получаю сообщение "Ошибка: неверное преобразование от std::unique_ptr<D>
до std::unique_ptr<D>&&
". Моя интуиция заявила, что в этом контексте компилятор должен знать, что он мог бы неявно сделать d
значение rvalue и переместить его в базовый указатель, но это не так.
Является ли это несоответствием в моих компиляторах (gcc 4.8.1 и VS2012)? Предполагаемая конструкция unique_ptr
? Дефект в стандарте?
Ответы
Ответ 1
Поведение компилятора корректно. Существует только неявный ход, когда типы являются одинаковыми, поскольку неявное перемещение задается в терминах компилятора, не выполняющего копирование в случаях, когда это фактически разрешено (см. 12.8/31 и 12.8/32).
12.8/31 (копия elision):
в операторе return в функции с типом возвращаемого класса, когда выражение является именем энергонезависимого автоматического объекта (отличным от параметра функции или параметра catch-clause) с тем же самым cv-неквалифицированным типом как возвращаемый тип функции...
12.8/32 (неявный ход):
Когда критерии для выполнения операции копирования выполняются, [...], разрешение перегрузки для выбора конструктора для копии сначала выполняется , как если бы объект был обозначен rvalue.
Ответ 2
С добавлением вызовов std::move
в операторах return
, этот код работает для меня на 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! ***
}
Также работает
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! ***
}
Конечно, если вы все равно возвращаете std::unique_ptr<B>
, почему бы не переместить экземпляр D
в std::unique_ptr<B>
в первую очередь? Тогда нет никакого преобразования вообще!