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

You*_*008 21 c++ inheritance templates unique-ptr

我编写了以下代码,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;
}
Run Code Online (Sandbox Code Playgroud)

我希望这段代码不会编译,因为根据我的理解unique_ptr<Base>unique_ptr<Derived>它们是不相关的类型,并且unique_ptr<Derived>实际上不是从中派生的,unique_ptr<Base>因此赋值不起作用。

但是多亏了一些魔术,它才能起作用,而且我不知道为什么,即使这样做是安全的。有人可以解释一下吗?

Que*_*tin 25

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

template<class U, class E>
unique_ptr(unique_ptr<U, E> &&u) noexcept;
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

son*_*yao 13

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

template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept;
Run Code Online (Sandbox Code Playgroud)

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

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


lub*_*bgr 7

您可以隐式构造一个std::unique_ptr<T>从一个实例右值std::unique_ptr<S>时候S可以转换为T。这是由于构造器#6 这里。在这种情况下,所有权被转移。

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