web-dev-qa-db-ja.com

unique_ptr <Base>からunique_ptr <Derived>への「ダウンキャスティング」

unique_ptr<Base>を返す一連の工場があります。ただし、内部では、さまざまな派生型、つまりunique_ptr<Derived>unique_ptr<DerivedA>unique_ptr<DerivedB>etcへのポインターを提供しています。

DerivedA : DerivedDerived : Baseが与えられた場合:

unique_ptr<Base> DerivedAFactory() {
    return unique_ptr<Base>(new DerivedA);
}

私がする必要があるのは、返されたunique_ptr<Base>から何らかの派生レベル(必ずしも元の内部レベルではない)にポインターを「キャスト」することです。擬似コードで説明するには:

unique_ptr<Derived> ptr = static_cast<unique_ptr<Derived>>(DerivedAFactory());

私はこれをunique_ptrからオブジェクトを解放し、生のポインターをキャストし、それを目的のフレーバーの別のunique_ptrrelease呼び出しの前に呼び出し元によって明示的に行われます):

unique_ptr<Derived> CastToDerived(Base* obj) {
    return unique_ptr<Derived>(static_cast<Derived*>(obj));
}

これは有効ですか、それともファンキーなことが起こっていますか?


PS。いくつかのファクトリーが実行時に動的にロードされるDLLに存在するという点で複雑さが増します。つまり、作成されたオブジェクトが作成されたのと同じコンテキスト(ヒープスペース)で破壊されることを確認する必要があります。所有権の譲渡(通常は別のコンテキストで発生します)は、元のコンテキストから削除者を提供する必要があります。ただし、ポインターとともに削除機能を提供/キャストする必要があることを除いて、キャストの問題は同じである必要があります。

53
d7samurai

static_unique_ptr_castdynamic_unique_ptr_castという2つの関数テンプレートを作成します。ポインターが実際にDerived *であることが確実な場合は前者を使用し、そうでない場合は後者を使用します。

template<typename Derived, typename Base, typename Del>
std::unique_ptr<Derived, Del> 
static_unique_ptr_cast( std::unique_ptr<Base, Del>&& p )
{
    auto d = static_cast<Derived *>(p.release());
    return std::unique_ptr<Derived, Del>(d, std::move(p.get_deleter()));
}

template<typename Derived, typename Base, typename Del>
std::unique_ptr<Derived, Del> 
dynamic_unique_ptr_cast( std::unique_ptr<Base, Del>&& p )
{
    if(Derived *result = dynamic_cast<Derived *>(p.get())) {
        p.release();
        return std::unique_ptr<Derived, Del>(result, std::move(p.get_deleter()));
    }
    return std::unique_ptr<Derived, Del>(nullptr, p.get_deleter());
}

関数は、stealing渡されたunique_ptrによって呼び出し元の足の下からラグを引き抜かないようにするために右辺値参照を取得しています。

41
Praetorian