为什么unique_ptr <Derived>隐式转换为unique_ptr <Base>?


21

我编写了以下代码,unique_ptr<Derived>其中使用了unique_ptr<Base>预期的a

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<Base>unique_ptr<Derived>它们是不相关的类型,并且unique_ptr<Derived>实际上不是从中派生的,unique_ptr<Base>因此该赋值不应起作用。

但是,由于某种神奇的功能,它起作用了,我也不知道为什么,或者即使这样做是安全的。有人可以解释一下吗?


3
智能指针是为了丰富指针不能限制的内容。如果没有这种可能,unique_ptr那么在继承的情况下将毫无用处
idclev 463035818 '19

3
“但是要感谢一些魔术,它才有用”。几乎,您有了UB,因为Base它没有虚拟析构函数。
Jarod42

Answers:


25

您正在寻找的神奇之处是这里的转换构造函数#6 :

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

它允许std::unique_ptr<T>从到期std::unique_ptr<U> if隐式构造(为清楚起见,在删除器上进行光泽处理):

unique_ptr<U, E>::pointer 可隐式转换为 pointer

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


2
AFAIK的删除器Base不会调用的析构函数Derived,因此我不确定它是否真的安全。(诚​​然,它的安全性不低于原始指针。)
cpplearner

14

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

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

如果满足以下所有条件,则此构造方法仅参与重载解析:

a)unique_ptr<U, E>::pointer可隐式转换为pointer

...

A Derived*可以Base*隐式转换为,然后可以在这种情况下应用转换构造函数。然后std::unique_ptr<Base>可以std::unique_ptr<Derived>像原始指针一样从隐式转换a 。(请注意,由于的特性,std::unique_ptr<Derived>必须将其作为构造的右值。)std::unique_ptr<Base>std::unique_ptr


7

您可以根据何时可转换为的隐式构造std::unique_ptr<T>实例。这是由于这里的#6构造函数。在这种情况下,所有权被转移。std::unique_ptr<S>ST

在您的示例中,您只有类型的右std::uinque_ptr<Derived>值(因为的返回值std::make_unique是右值),当您将其用作a时std::unique_ptr<Base>,将调用上述构造函数。std::unique_ptr<Derived>因此,所讨论的对象仅生存很短的时间,即它们被创建,然后所有权被传递给std::unique_ptr<Base>对象,该对象将被进一步使用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.