web-dev-qa-db-ja.com

unique_ptrを基本クラスに渡す関数の引数としての派生クラスへのunique_ptr

unique_ptrを基本クラスに取る関数で、unique_ptrを派生クラスに使用しようとしています。何かのようなもの:

class Base {};

class Derived : public Base {};

void f(unique_ptr<Base> const &base) {}

…

unique_ptr<Derived> derived = unique_ptr<Derived>(new Derived);
f(derived);

この回答 を正しく理解していれば、このコードは機能するはずですが、次のコンパイルエラーが発生します。

エラーC2664: 'f':パラメーター1を 'std :: unique_ptr <_Ty>'から 'const std :: unique_ptr <_Ty>&'に変換できません

IntelliSense:「std :: unique_ptr <Derived、std :: default_delete <Derived >>」から「const std :: unique_ptr <Base、std :: default_delete <Base >>」への適切なユーザー定義の変換は存在しません

funique_ptr<Derived> const &derivedに変更すると、うまくいきますが、それは私が望んでいることではありません。

私は何か間違っていますか?これを回避するにはどうすればよいですか?

Visual Studio 2012を使用しています。

54
svick

次の3つのオプションがあります。

  1. 所有権を放棄します。これにより、関数呼び出し後、ローカル変数は動的オブジェクトにアクセスできなくなります。オブジェクトが呼び出し先に転送されました:

    f(std::move(derived));
    
  2. fの署名を変更します。

    void f(std::unique_ptr<Derived> const &);
    
  3. 変数のタイプを変更します。

    std::unique_ptr<base> derived = std::unique_ptr<Derived>(new Derived);
    

    または、もちろんただ:

    std::unique_ptr<base> derived(new Derived);
    

    あるいは:

    std::unique_ptr<base> derived = std::make_unique<Derived>();
    
  4. 更新:または、コメントで推奨されているように、所有権をまったく譲渡しないでください:

    void f(Base & b);
    
    f(*derived);
    
59
Kerrek SB

可能な解決策は、引数の型をBase const*に変更し、代わりにderived.get()を渡すことです。 unique_ptr const<Base>&による所有権の譲渡はありません(また、unique_ptrは変更されていません)。したがって、Base const*に変更しても意味は変わりません。


ハーブサッターは、スマートポインター引数を スマートポインターパラメーター で詳しく説明しています。リンクされた記事からの抜粋は、この正確な状況を示しています。

const unique_ptr<widget>&を渡すことは、nullまたはunique_ptrを介して呼び出しコードでライフタイムが管理されるwidgetのいずれかのみを受け入れることができ、呼び出し先は通常、呼び出し元のライフタイム管理の選択を気にしないため、奇妙ですwidget*を渡すと、これらのケースの厳密なスーパーセットがカバーされ、呼び出し側が使用しているライフタイムポリシーに関係なく、「nullまたはwidget」を受け入れることができます。

9
hmjd

私は受け入れられた答えのオプション#1を持っていましたが、私はまだ同じコンパイルエラーがありました。 1時間以上壁に頭を叩きました

class Derived : Base {};

の代わりに

class Derived : public Base {};
1
David