【问题标题】:Why does unique_ptr<Derived> implicitly cast to unique_ptr<Base>?为什么 unique_ptr<Derived> 隐式转换为 unique_ptr<Base>?
【发布时间】:2020-01-30 12:39:27
【问题描述】:

我编写了以下使用 unique_ptr&lt;Derived&gt; 的代码,其中应该是 unique_ptr&lt;Base&gt;

class Base {
    int i;
 public:
    Base( int i ) : i(i) {}
    int getI() const { return i; }
};

class Derived : public Base {
    float f;
 public:
    Derived( int i, float f ) : Base(i), f(f) {}
    float getF() const { return f; }
};

void printBase( unique_ptr<Base> base )
{
    cout << "f: " << base->getI() << endl;
}

unique_ptr<Base> makeBase()
{
    return make_unique<Derived>( 2, 3.0f );
}

unique_ptr<Derived> makeDerived()
{
    return make_unique<Derived>( 2, 3.0f );
}

int main( int argc, char * argv [] )
{
    unique_ptr<Base> base1 = makeBase();
    unique_ptr<Base> base2 = makeDerived();
    printBase( make_unique<Derived>( 2, 3.0f ) );

    return 0;
}

我预计这段代码不会编译,因为根据我的理解 unique_ptr&lt;Base&gt;unique_ptr&lt;Derived&gt; 是不相关的类型,unique_ptr&lt;Derived&gt; 实际上并不是从 unique_ptr&lt;Base&gt; 派生的,所以分配不应该工作。

但多亏了一些魔法,它起作用了,我不明白为什么,或者即使这样做是安全的。 谁能解释一下?

【问题讨论】:

  • 智能指针是为了丰富指针的功能,而不是限制它。如果这不可能,unique_ptr 在存在继承的情况下将毫无用处
  • “但多亏了它的一些魔法”。几乎,你得到了 UB,因为 Base 没有虚拟析构函数。

标签: c++ templates inheritance unique-ptr


【解决方案1】:

您正在寻找的魔法是转换构造函数 #6 here:

template<class U, class E>
unique_ptr(unique_ptr<U, E> &&u) noexcept;

它允许从即将过期的std::unique_ptr&lt;U&gt; 隐式构造std::unique_ptr&lt;T&gt; if(为清楚起见,对删除器进行了修饰):

unique_ptr&lt;U, E&gt;::pointer 可隐式转换为 pointer

也就是说,它模仿隐式原始指针转换,包括派生到基的转换,并安全地执行您所期望的™(就生命周期而言 - 您仍然需要确保可以多态地删除基类型) .

【讨论】:

  • AFAIK Base 的删除器不会调用Derived 的析构函数,所以我不确定它是否真的安全。 (诚​​然,它的安全性不亚于原始指针。)
【解决方案2】:

因为std::unique_ptr 有一个转换构造函数

template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept;

这个构造函数只参与重载决议,如果所有 以下是正确的:

a) unique_ptr&lt;U, E&gt;::pointer 可隐式转换为 pointer

...

Derived* 可以隐式转换为Base*,然后可以将转换构造函数应用于这种情况。然后 std::unique_ptr&lt;Base&gt; 可以像原始指针一样隐式地从 std::unique_ptr&lt;Derived&gt; 转换。 (注意std::unique_ptr&lt;Derived&gt;必须是一个右值来构造std::unique_ptr&lt;Base&gt;,因为std::unique_ptr的特性。)

【讨论】:

    【解决方案3】:

    只要S 可转换为T,您就可以隐式std::unique_ptr&lt;S&gt;右值 构造std::unique_ptr&lt;T&gt; 实例。这是由于构造函数#6 here。在这种情况下,所有权被转移。

    在您的示例中,您只有std::uinque_ptr&lt;Derived&gt; 类型的右值(因为std::make_unique 的返回值是一个右值),当您将其用作std::unique_ptr&lt;Base&gt; 时,将调用上述构造函数。因此,有问题的 std::unique_ptr&lt;Derived&gt; 对象只存在很短的时间,即它们被创建,然后所有权被传递给 std::unique_ptr&lt;Base&gt; 对象,以便进一步使用。

    【讨论】:

      猜你喜欢
      • 2018-12-15
      • 2014-03-27
      • 2019-05-24
      • 1970-01-01
      • 2016-07-23
      • 2020-07-17
      • 2021-10-09
      • 2015-08-08
      • 2020-09-29
      相关资源
      最近更新 更多