Let's say I have the following hierarchy of classes:
struct Base
{
};
struct Derived : public Base
{
void DoStuffSpecificToDerivedClass()
{
}
};
And the following factory method:
std::unique_ptr<Base> factoryMethod()
{
auto derived = std::make_unique<Derived>();
derived->DoStuffSpecificToDerivedClass();
return derived; // does not compile
}
The problem is, the return
statement does not compile, because std::unique_ptr
does not have a copy constructor with covariance support (which makes sense since it does not have any copy constructors), it only has a move constructor with covariance support.
What is the best way to make solve this problem? I can think of two ways:
return std::move(derived); // this compiles
return std::unique_ptr<Base>(derived.release()); // and this compiles too
EDIT 1: I'm using Visual C++ 2013 as my compiler. The original error message for return derived
looks like this:
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>> &&'
EDIT 2: It is a freshly created console app from a standard VS 2013 template. I haven't tweaked any compiler settings. Compiler command line looks like this:
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"
std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. The object is disposed of, using the associated deleter when either of the following happens: the managing unique_ptr object is destroyed.
If a function returns a std::unique_ptr<> , that means the caller takes ownership of the returned object. class Base { ... }; class Derived : public Base { ... }; // Foo takes ownership of |base|, and the caller takes ownership of the returned // object.
std::unique_ptr::getReturns the stored pointer. The stored pointer points to the object managed by the unique_ptr, if any, or to nullptr if the unique_ptr is empty.
A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it.
You can do this:
return std::move(derived);
That way you tell the compiler no copy is needed, which satisfies the requirements of unique_ptr
. If the types matched perfectly you should not need to explicitly specify move
, but in this case you do.
As stated in the question, the problem is, the return statement does not compile, std::unique_ptr
does not have a copy constructor with covariance support, it only has a move constructor with covariance support, however, compiler still doesn't move from std::unique_ptr<Derived>
.
It is because conditions for moving from an object returned from a function are tied closely to the criteria for copy elision, which strictly requires that type of the object being returned need to be same as the return type of the function.
[class.copy]/32:
When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.
Therefore, I prefer,
return std::move(derived);
However, there is rule change in DR-9R5 so that the return value will be treated as an rvalue even when the types are not the same, gcc-5 implemented the rule and you don't need to change your code for gcc-5 as shown here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With