RX_DID_RX
RX_DID_RX

Reputation: 4183

The correct way of returning std::unique_ptr to an object of polymorphic class

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" 

Upvotes: 16

Views: 19370

Answers (2)

Alper
Alper

Reputation: 13220

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.

Upvotes: 7

John Zwinck
John Zwinck

Reputation: 249293

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.

Upvotes: 11

Related Questions