返回std :: make_unique <subclass>如何工作?

时间:2016-09-13 21:03:43

标签: c++ move unique-ptr

我有一个基类及其子类:

std::unique_ptr<Base> GetDerived() {
    return std::make_unique<Derived>(); 
}

尝试创建一个辅助函数,该函数创建Derived对象的唯一指针。

1)这个有效:

std::unique_ptr<Base> GetDerived2() { 
    auto a = std::make_unique<Derived>(); 
    return a; 
}

2)但是,这个没有编译:

std::unique_ptr<Base> GetDerived3() {
    auto a = std::make_unique<Derived>();
    return std::move(a); 
}

3)std :: move有效:

std::unique_ptr<Base> GetDerived4() {
    auto a = std::make_unique<Base>();
    return a; 
}

std::unique_ptr<Base> GetDerived5() {
    auto a = std::make_unique<Base>();
    return std::move(a); 
}

4)如果我创建了一个Base实例,那么两者都有效:

ImportError: No module named 'winrandom'

为什么(2)失败但其他人工作?

5 个答案:

答案 0 :(得分:10)

std::unique_ptr不可复制,只能移动。您可以return std::make_unique<Derived>从声明返回std::unique_ptr<Base>的函数中获取的原因是,从一个函数转换为另一个函数。

所以1)相当于:

std::unique_ptr<Base> GetDerived() {
    return std::unique_ptr<Base>(std::made_unique<Derived>());
}

由于std::make_unique返回的值是一个右值,因此返回值是移动构造的。

对比2),相当于:

std::unique_ptr<Base> GetDerived2() { 
    std::unique_ptr<Derived> a = std::make_unique<Derived>(); 
    return std::unique_ptr<Base>(a); 
}

由于a是左值,因此返回值必须是复制构造的,std::unique_ptr是不可复制的。

3)之所以有效是因为你将左值a转换为右值,并且返回值可以移动构造。

4)和5)工作是因为你已经有std::unique_ptr<Base>并且不需要构建一个返回。

答案 1 :(得分:1)

std::unique_ptr<>没有复制构造函数,但它确实有一个来自相关指针的移动构造函数,即

unique_ptr( unique_ptr&& u );         // move ctor
template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u );   // move ctor from related unique_ptr

第二个构造函数需要某些条件(参见here)。那么为什么你的代码2不起作用,但是4呢?在4中,您没有使用任何构造函数,因为返回类型与对象相同,返回了对象本身。另一方面,在2中,返回类型不同,需要构造函数调用,但需要std::move()

答案 2 :(得分:1)

在每种情况下,但(2)返回的值被视为(某种)rvalue。在(2)中它不是,因为类型不匹配隐式移动被阻止。

在标准的后续迭代中,(2)也会隐式移动。

所有这些都会在被调用后很快进入未定义的行为,因为他们试图通过指针 - Derived删除Base对象。要解决此问题,请记录删除功能。

template<class T>
using smart_unique=std::unique_ptr<T, void(*)(void*)>;

template<class T, class...Args>
smart_unique<T> make_smart_unique( Args&&... args ){
  return {
    new T(std::forward<Args>(args)...),
    [](void*ptr){ delete static_cast<T*>(ptr); }
  };
}
template<class T>
static const smart_unique<T> empty_smart_unique{ nullptr, [](void*){} };

这些是足以处理多态性的独特指针,如shared_ptr可以。

答案 3 :(得分:0)

在上面列出的示例中,(1)返回一个右值,但(2)不是右值,并且正在尝试对unique_ptr进行复制,而对于unique_ptr则无法复制。

使用move是有效的,因为您将此时的unique_ptr视为右值。

答案 4 :(得分:-1)

如果您仔细研究std :: unique_ptr的定义,就会发现它可以从派生的转移到基数。基本上is_convertible在这里检查这种情况。

   /** @brief Converting constructor from another type
   *
   * Requires that the pointer owned by @p __u is convertible to the
   * type of pointer owned by this object, @p __u does not own an array,
   * and @p __u has a compatible deleter type.
   */
  template<typename _Up, typename _Ep, typename = _Require<
           __safe_conversion_up<_Up, _Ep>,
       typename conditional<is_reference<_Dp>::value,
                is_same<_Ep, _Dp>,
                is_convertible<_Ep, _Dp>>::type>>
unique_ptr(unique_ptr<_Up, _Ep>&& __u) noexcept
: _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter()))
{ }