shared_ptr <Derived>をshared_ptr <Base>として渡す


95

shared_ptr派生型shared_ptrのaを基本型のaを取る関数に渡すための最良の方法は何ですか?

shared_ptr不必要なコピーを避けるために、私は通常、参照によってsを渡します。

int foo(const shared_ptr<bar>& ptr);

しかし、私が次のようなことをしようとすると、これは機能しません

int foo(const shared_ptr<Base>& ptr);

...

shared_ptr<Derived> bar = make_shared<Derived>();
foo(bar);

使用できます

foo(dynamic_pointer_cast<Base, Derived>(bar));

しかし、これは2つの理由で最適ではないようです。

  • Aはdynamic_cast、単純なため、過剰なビットが得ツーベースキャストようです。
  • 私が理解しているdynamic_pointer_castように、関数に渡すポインターのコピー(一時的なものではありますが)を作成します。

より良い解決策はありますか?

後世のための更新:

ヘッダーファイルがないという問題であることが判明しました。また、ここで私がやろうとしていたことは、アンチパターンと見なされます。一般的に、

  • オブジェクトの存続期間に影響を与えない(つまり、オブジェクトが関数の期間中有効である)関数は、単純な参照またはポインターを使用する必要がありますint foo(bar& b)

  • オブジェクトを消費する関数(つまり、特定のオブジェクトの最終ユーザー)は、unique_ptrby値を取る必要がありますint foo(unique_ptr<bar> b)。呼び出し元はstd::move、関数に値を入力する必要があります。

  • オブジェクトの存続期間を延長する関数はshared_ptr、値をとる必要がありますint foo(shared_ptr<bar> b)循環参照を避けるための通常のアドバイスが適用されます。

詳細については、ハーブサッターの「基本に立ち返る」の話を参照してください。


8
なぜあなたは合格したいのshared_ptrですか?なぜバーのconst-referenceがないのですか?
ipc 2012年

2
任意のdynamicキャストが唯一のダウンキャストのために必要とされます。また、派生ポインタを渡すことは問題なく機能するはずです。shared_ptr同じrefcount(およびそれを増やす)とベースへのポインターを使用してnewを作成し、それがconst参照にバインドします。しかし、あなたはすでに参考文献を取っているので、なぜあなたが参考にしたいのか全くわかりませんshared_ptr。を取り、Base const&を呼び出しますfoo(*bar)
xeo 2012年

@Xeo:派生ポインタを渡す(つまりfoo(bar))し、少なくともMSVC 2010年のない仕事、
マット・クライン

1
「明らかに機能しない」とはどういう意味ですか?コードはコンパイルされ、正しく動作します。shared_ptr関数に渡す一時的なものを作成しないようにする方法を尋ねていますか?それを回避する方法はないと確信しています。
マイクシーモア

1
@Seth:同意しません。共有ポインターを値で渡す理由はあると思いますが、共有ポインターを参照で渡す理由はほとんどありません(そして、これらすべてが不要なコピーを推奨することなく)。ここでの理由stackoverflow.com/questions/10826541/…–
R. Martinho Fernandes

回答:


48

けれどもBaseDerivedされているそれらの共変と生のポインタはそれに応じて行動する、うshared_ptr<Base>shared_ptr<Derived>しているではない共変。これdynamic_pointer_castは、この問題を処理するための正確で最も簡単な方法です。

編集: static_pointer_cast派生からベースにキャストするため、より適切です。これは安全で、ランタイムチェックを必要としません。以下のコメントを参照してください。)

ただし、foo()関数が存続期間の延長に参加したくない場合(または、オブジェクトの共有所有権に参加したくない場合)は、を受け入れて、const Base&shared_ptr渡すときに間接参照するのが最善foo()です。

void foo(const Base& base);
[...]
shared_ptr<Derived> spDerived = getDerived();
foo(*spDerived);

余談ですが、shared_ptr型は共変ではないため、共変の戻り型をまたがる暗黙の変換の規則は、の型を返すときに適用されませんshared_ptr<T>


39
それらは共変ではありませんshared_ptr<Derived>が、暗黙的にに変換可能shared_ptr<Base>であるため、コードはキャストシェナニガンなしで機能するはずです。
マイクシーモア

9
ええと、が暗黙的にに変換可能である場合、shared_ptr<Ty>を取りshared_ptr<Other>、適切な変換を行うコンストラクターがあります。そして、キャストが必要な場合は、ここでは適切ではありません。Ty*Other*static_pointer_castdynamic_pointer_cast
ピートベッカー

本当ですが、質問のように、彼の参照パラメーターではそうではありません。とにかく、彼はコピーをする必要があるでしょう。しかし、彼shared_ptrが参照カウントを回避するために'sへの参照を使用している場合、そもそもを使用する正当な理由はありませんshared_ptrconst Base&代わりに使用することをお勧めします。
Bret Kuhns 2012年

@PeteBecker変換コンストラクターについてのMikeへの私のコメントを参照してください。正直static_pointer_cast、知りませんでした、ありがとう。
Bret Kuhns 2012年

1
@TanveerBadarわからない。おそらくこれは2012年にコンパイルに失敗しましたか?(特にVisual Studio 2010または2012を使用)。しかし、あなたは絶対に正しいです。/publicly派生/クラスの完全な定義がコンパイラに見える場合、OPのコードは絶対にコンパイルする必要があります。
BretKuhns20年

34

これは、派生クラスでパブリック継承を指定するのを忘れた場合にも発生します。つまり、私のように次のように記述します。

class Derived : Base
{
};

classテンプレートパラメータ用です。structクラスを定義するためのものです。(これはせいぜい45%の冗談です。)
DavisHerring19年

これは間違いなく解決策と見なされるべきであり、公開されていないだけなのでキャストの必要はありません。
AlexisPaques20年

12

一生懸命頑張っているようですね。shared_ptrコピーするのは安いです。それがその目標の1つです。参照によってそれらを渡すことは実際には多くを達成しません。共有したくない場合は、生のポインタを渡します。

そうは言っても、これを行うには2つの方法があります。頭のてっぺんから考えることができます。

foo(shared_ptr<Base>(bar));
foo(static_pointer_cast<Base>(bar));

9
いいえ、コピーするのは安くはありません。可能な限り参照して渡す必要があります。
セスカーネギー

6
@ SethCarnegie-値の受け渡しがボトルネックであったかどうかを確認するために、Herbはコードをプロファイリングしましたか?
ピートベッカー

25
@ SethCarnegie-それは私が尋ねた質問に答えません。そして、その価値のために、私はshared_ptrMicrosoftが出荷する実装を書きました。
ピートベッカー

6
@ SethCarnegie-ヒューリスティックを後方に持っています。手の最適化は、必要であることを示すことができない限り、通常実行しないでください。
ピートベッカー

21
あなたがそれに取り組む必要がある場合、それは「時期尚早な」最適化だけです。特定の状況で違いが生じるかどうかにかかわらず、非効率的なイディオムよりも効率的なイディオムを採用することに問題はありません。
マークランサム

11

また#include、派生クラスの完全な宣言を含むヘッダーファイルのがソースファイルにあることを確認してください。

私はこの問題を抱えていました。にstd::shared<derived>キャストしませんstd::shared<base>。両方のクラスへのポインターを保持できるように両方のクラスを前方宣言しましたが#include、コンパイラーがなかったため、コンパイラーは一方のクラスが他方から派生したことを認識できませんでした。


1
うわー、私はそれを予期していませんでしたが、これは私のためにそれを修正しました。私は特に注意を払い、必要な場所にのみヘッダーファイルをインクルードしていたので、それらの一部はソースファイルにのみ含まれ、あなたが言ったようにヘッダーでフォワード宣言しました。
プリン

愚かなコンパイラは愚かです。これが私の問題でした。ありがとうございました!
TanveerBadar20年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.