メインコンテンツへスキップ

スマートポインタをdynamic_castしたい

·683 文字·
技術解説 C++ STL
komori-n
著者
komori-n
目次

スマートポインタ (std::unique_ptrやstd::shared_ptr)を dynamic_cast する方法を意外と忘れやすいのでメモ。

背景
#

クラス DerivedBase を継承している状況を考える。

class Base {
public:
    virtual int foo(int x) { return x; }
};

class Derived {
public:
    virtual int foo(int x) override { return 2 * x; }
};

次のようなポインタ変換を行いたい。

  • std::shared_ptr<Derived> -> std::shared_ptr<Base>
  • std::unique_ptr<Derived> -> std::unique_ptr<Base>

shared_ptr の dynamic_cast
#

shread_ptrの場合、ズバリ std::dynamic_pointer_cast という関数が使える1

std::shared_ptr<Derived> derived = std::make_shared<Derived>();
std::shared_ptr<Base> base = std::dynamic_pointer_cast<Base>(derived);

この関数は参照カウンタを保ったままポインタの変換を行ってくれる。変換前、変換後の shared_ptr は同じ参照カウンタを共有しており、両方から参照されなくなったタイミンングで自動的にメモリが開放される。

int main(void) {
    std::shared_ptr<Derived> derived = std::make_shared<Derived>();
    {
        std::shared_ptr<Base> base = std::dynamic_pointer_cast<Base>(derived);
        // 2
        std::cout << derived.use_count() << std::endl;
    }
    // 1
    std::cout << derived.use_count() << std::endl;
    return 0;

    // derived のメモリが開放される
}

なお、dynamic_castに失敗したときは参照カウンタは増加せず、空の shared_ptr が返る。

int main(void) {
    std::shared_ptr<Base> base= std::make_shared<Base>();
    {
        // (Derived*)base の変換に失敗するので、derivedの中身は空になる
        std::shared_ptr<Derived> derived = std::dynamic_pointer_cast<Derived>(base);

        // 1  <- 変換に失敗したのでカウンタは増えない
        std::cout << base.use_count() << std::endl;
    }
    // 1
    std::cout << base.use_count() << std::endl;
    return 0;

    // derived のメモリが開放される
}

似たような関数として、 std::static_pointer_caststd::reinterpret_pointer_cast も用意されている。

unique_ptr の dynamic_cast
#

unique_ptrに対しては std::dynamic_pointer_cast のような便利関数は存在しない。しかし、以下のようにして簡単に変換関数を自作することができる。

template <typename U, typename T>
std::unique_ptr<U> dynamic_unique_cast(std::unique_ptr<T>&& ptr) {
    return std::unique_ptr<U>(dynamic_cast<U*>(ptr.release()));
}

release() で元のスマートポインタから生ポインタを剥ぎ取り、変換してから std::unique_ptr へ詰め直している。custom deleterを使いたい場合は、お好みで書き換えればよい。

Related

安定な優先順位付きキュー(stable_priority_queue)を作る
·2340 文字
技術解説 C++ STL
move-onlyなlambda式のTaskQueueを作る
·1234 文字
技術解説 C++ STL
std::recursive_mutexを使う
·1067 文字
技術解説 C++ STL