将std :: unique_ptr <derived>转换为std :: unique_ptr <base /> </derived>

时间:2014-02-25 15:05:30

标签: c++ c++11 unique-ptr c++14

假设我有处理基类和派生类的工厂函数:

#include <memory>

using namespace std;

struct B { virtual ~B() {} };
struct D : B {};

unique_ptr<B> MakeB()
{
    auto b = unique_ptr<B>( new B() );
    return b; // Ok!
}

unique_ptr<B> MakeD()
{
    auto d = unique_ptr<D>( new D() );
    return d; // Doh!
}

在上面的最后一行,我需要move(d)才能使其正常工作,否则我会收到“错误:从std::unique_ptr<D>std::unique_ptr<D>&&的转换无效。”我的直觉说,在这种情况下,编译器应该知道它可以隐式地使d成为一个rvalue并将其移动到基指针中,但事实并非如此。

这在我的编译器(gcc 4.8.1和VS2012)中是不可信的吗? unique_ptr的预期设计?标准中存在缺陷吗?

2 个答案:

答案 0 :(得分:33)

编译器的行为是正确的。当类型相同时,只有一个隐式移动,因为隐式移动是根据编译器在实际允许的情况下无法执行复制省略来指定的(见12.8 / 31和12.8 / 32)。

12.8 / 31(复制省略):

  

在具有类返回类型的函数的return语句中,当表达式是非易失性自动对象的名称(除了函数或catch子句参数)具有相同的cv-unqualified类型作为函数返回类型 ...

12.8 / 32(隐含动作):

  

当满足复制操作的省略标准时,首先执行重载决策以选择复制的构造函数,好像该对象是由右值指定

答案 1 :(得分:-4)

std::move语句中添加了return调用,此代码适用于Visual Studio 2013:

#include <memory>

using namespace std;

struct B { virtual ~B() {} };
struct D : B {};

unique_ptr<B> MakeB()
{
  auto b = unique_ptr<B>( new B() );
  return std::move( b );  // *** std::move() added here! ***
}

unique_ptr<B> MakeD()
{
  auto d = unique_ptr<D>( new D() );
  return std::move( d );  // *** std::move() added here! ***
}

以下也适用

unique_ptr<B> MakeB()
{
  return unique_ptr<B>( new B() );  // *** Returning a temporary! ***
}

unique_ptr<B> MakeD()
{
  return unique_ptr<D>( new D() );  // *** Returning a temporary! ***
}

当然,如果您还是要返回std::unique_ptr<B>,为什么不首先将D实例推送到std::unique_ptr<B>?然后根本不需要转换!