将std :: unique_ptr返回给多态类对象的正确方法

RX_*_*_RX 14 c++ smart-pointers unique-ptr

假设我有以下类的层次结构:

struct Base 
{
};

struct Derived : public Base 
{ 
    void DoStuffSpecificToDerivedClass() 
    {
    } 
};
Run Code Online (Sandbox Code Playgroud)

以下工厂方法:

std::unique_ptr<Base> factoryMethod()
{
    auto derived = std::make_unique<Derived>();
    derived->DoStuffSpecificToDerivedClass();
    return derived; // does not compile
}
Run Code Online (Sandbox Code Playgroud)

问题是,return语句没有编译,因为std::unique_ptr没有具有协方差支持的复制构造函数(这是有道理的,因为它没有任何复制构造函数),它只有一个具有协方差支持的移动构造函数.

解决这个问题的最佳方法是什么?我可以想到两种方式:

return std::move(derived); // this compiles
return std::unique_ptr<Base>(derived.release()); // and this compiles too
Run Code Online (Sandbox Code Playgroud)

编辑1:我使用Visual C++ 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>> &&'
Run Code Online (Sandbox Code Playgroud)

编辑2:它是标准VS 2013模板中新创建的控制台应用程序.我没有调整任何编译器设置.编译器命令行如下所示:

调试:

/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" 
Run Code Online (Sandbox Code Playgroud)

发布:

/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" 
Run Code Online (Sandbox Code Playgroud)

Joh*_*nck 9

你可以这样做:

return std::move(derived);
Run Code Online (Sandbox Code Playgroud)

这样你告诉编译器不需要复制,这满足了要求unique_ptr.如果类型匹配完美,则不需要明确指定move,但在这种情况下,您可以.

  • “如果类型完美匹配,您不需要显式指定移动,但在这种情况下您需要” - 这回答了我的问题。我不知道只有当类型完美匹配时才会发生隐式移动。谢谢。 (2认同)

Alp*_*per 7

正如问题中所述,问题是,return语句没有编译,std::unique_ptr没有具有协方差支持的复制构造函数,它只有一个具有协方差支持的移动构造函数,但编译器仍然没有移动std::unique_ptr<Derived>.

这是因为从函数返回的对象移动的条件与复制省略的标准紧密相关,这严格要求返回的对象的类型需要与函数的返回类型相同.

[class.copy]/32:

当满足或将满足复制操作的省略标准时,除了源对象是函数参数这一事实,并且要复制的对象由左值指定,重载决策选择复制的构造函数是首先执行,好像对象是由右值指定的.

因此,我更喜欢,

return std::move(derived);
Run Code Online (Sandbox Code Playgroud)

但是,DR-9R5中存在规则更改,因此即使类型不同,返回值也将被视为右值,gcc-5实现了规则,您无需更改gcc-5的代码如图所示这里.