2016-03-20 8 views
9

static_pointer_castunique_ptrとすると、含まれているデータの共有所有権につながることをご理解ください。他の点で
は、私は何をしたいのは、次のとおりです。static_pointer_castの代わりにunique_ptr

unique_ptr<Base> foo = fooFactory(); 
// do something for a while 
unique_ptr<Derived> bar = static_unique_pointer_cast<Derived>(foo); 

はとにかくそれが単に禁止されているように、2つのunique_ptrとの結果が、同時に存在してはならないということをやって。
実際、static_unique_pointer_castのようなものは存在しません。

これまでのところ、これらの基本クラスへのポインタを格納する場合、それらをいくつかの派生クラスにキャストする必要があります(例として、タイプ消去に関するシナリオを想像してください)。私はshared_ptrを使用しました。私が上で述べたことのために。

とにかく、このような問題のためにshared_ptrに代わるものがあるかどうか、あるいはそのような問題が実際には最高の解決策であるかどうかを推測していました。

+0

' static_pointer_cast'は 'std :: shared_ptrタイプの引数に対してのみ定義されています' - それは 'unique_ptr'で全く使用できません –

+0

@MMはい、私は知っています。 'unique_ptr'のために。 – skypjack

+0

タイプ消去がダウンキャストにつながるのはなぜですか?基底クラスを派生型にキャストする必要がある場合は、非常に少数のケースを除いて、ほとんど例外なく、異なる問題を解決する必要があります。 – Jens

答えて

16

生のポインタ

あなたの問題の解決策は、生(非所有)のポインタを取得し、それをキャストすることである - そしてちょうど生のポインタがスコープ外に行かせ、残りのunique_ptr<Base> constrolにの寿命を聞かせて所有するオブジェクト。このよう

unique_ptr<Base> foo = fooFactory(); 

{ 
    Base* tempBase = foo.get(); 
    Derived* tempDerived = static_cast<Derived*>(tempBase); 
} //tempBase and tempDerived go out of scope here, but foo remains -> no need to delete 

Unique_pointer_cast

他のオプションは、別のunique_ptrをにそれをラップするunique_ptrrelease()機能を使用することです。この

template<typename TO, typename FROM> 
unique_ptr<TO> static_unique_pointer_cast (unique_ptr<FROM>&& old){ 
    return unique_ptr<TO>{static_cast<TO*>(old.release())}; 
    //conversion: unique_ptr<FROM>->FROM*->TO*->unique_ptr<TO> 
} 

unique_ptr<Base> foo = fooFactory(); 

unique_ptr<Derived> foo2 = static_unique_pointer_cast<Derived>(std::move(foo)); 

同様

これは単に答えの完全性のために生のポインタ

から古いポインタfoo

リファレンスを無効にすることを忘れないでください、このソリューションは、実際には小さな変更として提案されましたコメントのOPによって生ポインタの

生ポインタを使用するのと同様に、生ポインタをキャストして、逆参照によって参照を作成することができます。この場合、作成された参照の存続期間がunique_ptrの存続期間を超えないことを保証することが重要です。

サンプル:

unique_ptr<Base> foo = fooFactory(); 
Derived& bar = *(static_cast<Derived*>(foo.get())); 
//do not use bar after foo goes out of scope 

+1

逆さまにしないのはなぜですかそれらのテンプレートパラメータの順番と 'FROM'を' std :: static_pointer_cast'のように推測させますか? – aschepler

+0

@Anedarええ、明らかに私は生のポインタを扱いたくありませんが、とにかく私に良いヒントを与えました... 'static_unique_pointer_cast'の実装は面白いですが、残念なことに(なぜなら、一度無効にされると、現在の使用後に*将来の使用*のために再初期化されなければならないからです。そうすれば、コンテナの周りに膨大な量の作業が発生します。 – skypjack

+0

良い点、@aschepler、私はそれを追加しました。 – Anedar

6

私はunique_ptrをしてstatic_pointer_castを使用することが含まれているデータの共有所有権につながることを理解しています。

あなたがそれをひどく定義している場合のみ。明らかな解決策は、ソースオブジェクトが空になるようにオーナーシップを転送することです。

所有権を譲渡したくない場合は、生のポインタを使用してください。

2人のオーナーが必要な場合は、shared_ptrを使用してください。

あなたの質問は実際のキャスト操作の一部に過ぎず、部分的にはポインタの明確な所有権ポリシーが不足しているようです。複数の所有者が必要な場合は、どちらも同じタイプを使用するか、異なるタイプにキャストするかどうかにかかわらず、unique_ptrを使用しないでください。

とにかく、これは決して2つのunique_ptrが同時に存在してはならないため、単に禁止されています。
確かにstatic_unique_pointer_castのようなものは存在しません。

いいえ、それは存在しません。あなたがそれを必要とするならば、あなた自身で書くことは自明である(そして、あなたが独特の所有権の元気な意味を与えている限り)、それは存在しません。ポインタをrelease()でキャストしてキャストし、別のunique_ptrに入れてください。シンプルで安全。 「明白な」解決策は正しいことをしないshared_ptr、の場合ではないです

:同じポインタを所有している二つの異なるshared_ptrオブジェクトを作成します

shared_ptr<Derived> p2(static_cast<Derived*>(p1.get()); 

が、ドン所有権を共有しません(つまり、両方とも削除しようとして、未定義の動作を引き起こします)。

shared_ptrが最初に標準化されたとき、それを安全に行う方法がなかったので、static_pointer_castと関連するキャスト関数が定義されました。彼らは働くために、shared_ptrの簿記情報の実装の詳細にアクセスする必要がありました。この機能は常にされていた場合

shared_ptr<Derived> p2(p1, static_cast<Derived*>(p1.get()); 

しかし、C++ 11標準化プロセスshared_ptr中にあなたが簡単かつ安全にキャストを行うことができます「エイリアシングコンストラクタ」を添加することによって増強されましたshared_ptrの部分では、static_pointer_castが決して定義されていない可能性があります。

+0

まあ、*それはあなた自身で書くことは自明なので存在しません*実際にはSTLの他の部分(存在する)にも適用できますが、私はあなたの答えのポイントを得ました。 +1 – skypjack

+0

あなたは文章の終わりに「必要な場合は」逃しました。ユーザが正しく書き込むことが難しい/不可能な場合(これはshared_ptrキャストは元々当てはまりました)、またはそれらが簡単だが誰もがそれを再実装し続ける場合、標準に入れる価値があります。 unique_ptrキャストはどちらも簡単ですが、しばしば必要ではないため、どちらも当てはまりません。 –

+1

私は正直に言うと、私はXY問題に直面していると思うが、それでも問題は自分が私であれば他人が何をしたのか知ることは面白かった。 – skypjack

0

release()メンバーメソッドを呼び出す前の回答Anedarに何かを追加したいと思います。 std::unique_ptr<U>std::unique_ptr<T>に変換するためにdynamic_pointer_caststatic_pointer_castに加えて)を実装したい場合は、dynamic_castに障害が発生した場合(つまりnullptrを返した場合)に一意のポインタによって保護されたリソースが適切に解放されていることを確認する必要があります。それ以外の場合は、メモリリークが発生します。

コード

#include <iostream> 
#include <memory> 

template< typename T, typename U > 
inline std::unique_ptr<T> dynamic_pointer_cast(std::unique_ptr<U> &&ptr) { 
    U * const stored_ptr = ptr.release(); 
    T * const converted_stored_ptr = dynamic_cast< T * >(stored_ptr); 
    if (converted_stored_ptr) { 
     std::cout << "Cast did succeeded\n"; 
     return std::unique_ptr<T>(converted_stored_ptr); 
    } 
    else { 
     std::cout << "Cast did not succeeded\n"; 
     ptr.reset(stored_ptr); 
     return std::unique_ptr<T>(); 
    } 
} 

struct A { 
    virtual ~A() = default; 
}; 
struct B : A { 
    virtual ~B() { 
     std::cout << "B::~B\n"; 
    } 
}; 
struct C : A { 
    virtual ~C() { 
     std::cout << "C::~C\n"; 
    } 
}; 
struct D { 
    virtual ~D() { 
     std::cout << "D::~D\n"; 
    } 
}; 

int main() { 

    std::unique_ptr<A> b(new B); 
    std::unique_ptr<A> c(new C); 
    std::unique_ptr<D> d(new D); 

    std::unique_ptr<B> b1 = dynamic_pointer_cast< B, A >(std::move(b)); 
    std::unique_ptr<B> b2 = dynamic_pointer_cast< B, A >(std::move(c)); 
    std::unique_ptr<B> b3 = dynamic_pointer_cast< B, D >(std::move(d)); 
} 

出力(可能な順序):1を使用している場合CD

Cast did succeeded 
Cast did not succeeded 
Cast did not succeeded 
B::~B 
D::~D 
C::~C 

デストラクタは、呼び出されません。

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