web-dev-qa-db-ja.com

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

派生型の_shared_ptr_を基本型の_shared_ptr_をとる関数に渡すのに最適な方法は何ですか?

通常、不要なコピーを避けるために_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つの理由で次善のようです。

  • _dynamic_cast_は、単純な派生からベースへのキャストには少し過剰に思えます。
  • 私が理解しているように、_dynamic_pointer_cast_は、関数に渡すポインターのコピー(一時的なコピーですが)を作成します。

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

後世の更新:

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

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

  • オブジェクトを消費する関数(つまり、指定されたオブジェクトの最終ユーザー)は、値で_unique_ptr_を取る必要があります。 int foo(unique_ptr<bar> b)。呼び出し元は、関数に値を_std::move_する必要があります。

  • オブジェクトの寿命を延ばす関数は、値によって_shared_ptr_を取る必要があります。 int foo(shared_ptr<bar> b)循環参照 を避けるための通常のアドバイスが適用されます。

詳細については、Herb Sutterの Back to Basics talk を参照してください。

70
Matt Kline

BaseDerivedは共変であり、それらへの生のポインターはそれに応じて動作しますが、_shared_ptr<Base>_と_shared_ptr<Derived>_はnot共変です。 _dynamic_pointer_cast_は、この問題を処理するための正しい最も簡単な方法です。

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

ただし、foo()関数がライフタイムの延長に参加したくない場合(または、オブジェクトの共有所有権に参加したい場合)は、_const Base&_を受け入れるのが最善です。 foo()に渡すときに_shared_ptr_を逆参照します。

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

余談ですが、_shared_ptr_型は共変にできないため、_shared_ptr<T>_の型を返す場合、共変の戻り型間の暗黙的な変換の規則は適用されません。

39
Bret Kuhns

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

そうは言っても、これを行うには2つの方法があります。

foo(shared_ptr<Base>(bar));
foo(static_pointer_cast<Base>(bar));
11
Pete Becker

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

class Derived : Base
{
};
7
dshepherd

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

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

6
Phil Rosenberg