Подтвердить что ты не робот

Правильный способ возврата 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" 
4b9b3361

Ответ 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, как показано здесь.