unique_ptr <Derived>が暗黙的にunique_ptr <Base>にキャストするのはなぜですか?


21

期待されるunique_ptr<Derived>場所を使用する次のコードを書いたunique_ptr<Base>

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

3
「しかし、いくつかの魔法のおかげで機能します」。ほぼ、Base仮想デストラクタがないのでUBを取得しました。
Jarod42

回答:


25

あなたが探している魔法のビットは、変換コンストラクター#6 です

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

これはstd::unique_ptr<T>、期限切れになるstd::unique_ptr<U> 場合から暗黙的に構築することを可能にします(明確にするために、削除者に光沢を付けます):

unique_ptr<U, E>::pointer 暗黙的に変換可能 pointer

つまり、派生からベースへの変換を含む暗黙的な生のポインター変換を模倣し、期待どおりに安全に実行します(存続期間の観点から、ベースタイプを多態的に削除できることを確認する必要があります)。


2
AFAIKの削除者Baseはのデストラクタを呼び出さないDerivedので、本当に安全かどうかはわかりません。(確かに、生のポインタと
同じくらい

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>。(の特性上、はstd::unique_ptr<Derived>構築するstd::unique_ptr<Base>ための右辺値でなければならないことに注意してくださいstd::unique_ptr。)


7

がに変換可能であるときはいつでも、rvalueからインスタンスを暗黙的に構築できstd::unique_ptr<T>ます。これは、コンストラクタ#6が原因です。この場合、所有権は譲渡されます。std::unique_ptr<S>ST

この例では、タイプの右辺std::uinque_ptr<Derived>値のみがあり(の戻り値std::make_uniqueは右辺値であるため)、それをとして使用すると、std::unique_ptr<Base>上記のコンストラクターが呼び出されます。std::unique_ptr<Derived>したがって、問題のオブジェクトは短期間しか存続しません。つまり、std::unique_ptr<Base>オブジェクトは作成され、所有権はその後に使用されるオブジェクトに渡されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.