【问题标题】:The correct way of returning std::unique_ptr to an object of polymorphic class将 std::unique_ptr 返回到多态类对象的正确方法
【发布时间】:2015-11-28 12:28:57
【问题描述】:

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

struct Base 
{
};

struct Derived : public Base 
{ 
    void DoStuffSpecificToDerivedClass() 
    {
    } 
};

还有以下工厂方法:

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

编辑 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>> &&'

编辑 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" 

发布:

/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" 

【问题讨论】:

  • 用 VC++2013 编译得很好。发布 MCVE 并告诉我们您是如何调用编译器的。
  • @ChristianHackl 我已将编译器命令行信息添加到我的问题中。顺便说一句,C++ Shell 也不编译:cpp.sh/8lxwn
  • 仍然没有 MCVE。 stackoverflow.com/help/mcve
  • 这不应该编译。请参阅此相关问题stackoverflow.com/q/22018115/72178
  • 您需要指定DoStuffSpecificToDerivedClass 的异常安全性,因为它可能被假定为根本不会抛出。

标签: c++ smart-pointers unique-ptr


【解决方案1】:

你可以这样做:

return std::move(derived);

这样你告诉编译器不需要复制,这满足unique_ptr的要求。如果类型完全匹配,则无需显式指定move,但在这种情况下您需要。

【讨论】:

  • OP 已经知道这个解决方案。这显然对他不起作用。一切都在问题之中。
  • “如果类型完全匹配,您不需要明确指定移动,但在这种情况下您需要” - 这回答了我的问题。我不知道只有在类型完全匹配时才会发生隐式移动。谢谢。
【解决方案2】:

如问题所述,问题是,return 语句无法编译,std::unique_ptr 没有支持协方差的复制构造函数,它只有一个支持协方差的移动构造函数,但是,编译器仍然没有从std::unique_ptr&lt;Derived&gt;移动。

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

[class.copy]/32:

当满足或将满足省略复制操作的条件时 遇到源对象是函数参数这一事实, 并且要复制的对象由左值指定,重载 首先执行为副本选择构造函数的分辨率 就好像对象是由右值指定的一样。

因此,我更喜欢,

return std::move(derived);

但是,DR-9R5 中的规则发生了变化,因此即使类型不同,返回值也将被视为右值,gcc-5 实现了该规则,您无需更改代码gcc-5 如图here.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-19
    • 2013-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多