Правильный способ возврата std:: unique_ptr к объекту полиморфного класса
Скажем, у меня есть следующая иерархия классов:
struct Base
{
};
struct Derived : public Base
{
void DoStuffSpecificToDerivedClass()
{
}
};
И следующий метод factory:
std::unique_ptr<Base> factoryMethod()
{
auto derived = std::make_unique<Derived>();
derived->DoStuffSpecificToDerivedClass();
return derived; // does not compile
}
Проблема заключается в том, что оператор return
не компилируется, потому что std::unique_ptr
не имеет конструктора копирования с поддержкой ковариации (что имеет смысл, поскольку он не имеет каких-либо конструкторов), он имеет только конструктор перемещения с ковариационной поддержки.
Каков наилучший способ решить эту проблему? Я могу думать о двух путях:
return std::move(derived); // this compiles
return std::unique_ptr<Base>(derived.release()); // and this compiles too
EDIT 1: Я использую Visual С++ 2013 как мой компилятор. Исходное сообщение об ошибке для return derived
выглядит следующим образом:
Error 1 error C2664: 'std::unique_ptr<Base,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : cannot convert argument 1 from 'std::unique_ptr<Derived,std::default_delete<Derived>>' to 'std::unique_ptr<Derived,std::default_delete<Derived>> &&'
РЕДАКТИРОВАТЬ 2: Это только что созданное консольное приложение из стандартного шаблона VS 2013. Я не настраивал настройки компилятора. Командная строка компилятора выглядит так:
Debug:
/Yu"stdafx.h" /GS /analyze- /W3 /Zc:wchar_t /ZI /Gm /Od /sdl /Fd"Debug\vc120.pdb" /fp:precise /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_LIB" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /Oy- /MDd /Fa"Debug\" /EHsc /nologo /Fo"Debug\" /Fp"Debug\CppApplication1.pch"
Release:
/Yu"stdafx.h" /GS /GL /analyze- /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /sdl /Fd"Release\vc120.pdb" /fp:precise /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_LIB" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /Gd /Oy- /Oi /MD /Fa"Release\" /EHsc /nologo /Fo"Release\" /Fp"Release\CppApplication1.pch"
Ответы
Ответ 1
Вы можете сделать это:
return std::move(derived);
Таким образом, вы сообщаете компилятору, что не требуется копия, которая удовлетворяет требованиям unique_ptr
. Если типы соответствуют друг другу, вам не нужно явно указывать move
, но в этом случае вы делаете.
Ответ 2
Как указано в вопросе, проблема заключается в том, что оператор return не компилируется, std::unique_ptr
не имеет конструктора копирования с поддержкой ковариации, он имеет только конструктор перемещения с поддержкой ковариации, однако компилятор все еще не выполняет перейдите от std::unique_ptr<Derived>
.
Это связано с тем, что условия перехода от объекта, возвращаемого функцией, тесно связаны с критериями для копирования, что требует строгого требования к типу возвращаемого объекта, который должен быть таким же, как тип возвращаемого значения функции.
[class.copy]/32:
Когда критерии для исключения операции копирования выполняются или будут сэкономленные за тот факт, что исходный объект является параметром функции, и подлежащий копированию объект обозначается значением lvalue, перегрузкой разрешение для выбора конструктора для копии сначала выполняется как будто объект был обозначен rvalue.
Поэтому я предпочитаю
return std::move(derived);
Однако существует изменение правила в DR-9R5, так что возвращаемое значение будет рассматриваться как значение r, даже если типы не являются то же самое, gcc-5 выполнил правило, и вам не нужно менять код для gcc-5, как показано здесь.