web-dev-qa-db-ja.com

unique_ptrを使用してconstオブジェクトにconstポインターを渡す方法

_unique_ptr_をヘルパー関数に渡したいのですが、ヘルパー関数がポインターもポイントされたオブジェクトも変更しないようにしたいのです。 _unique_ptr_がなければ、解決策は

_void takePtr(AClass const * const aPtr) {
  // Do something with *aPtr. 
  // We cannot change aPtr, not *aPtr. 
}
_

(まあ、技術的には、_AClass const * aPtr_で十分です。)そして、

_AClass * aPtr2 = new AClass(3);
takePtr(aPtr2);
_

代わりに_unique_ptr_を使用したいのですが、これの書き方がわかりません。私は試した

_void takeUniquePtr(unique_ptr<AClass const> const & aPtr) {
  // Access *aPtr, but should not be able to change aPtr, or *aPtr. 
}
_

私がこれを呼ぶとき

_unique_ptr<AClass> aPtr(new AClass(3));
takeUniquePtr(aPtr);
_

コンパイルされません。私が見るエラーは

_testcpp/hello_world.cpp:141:21: error: invalid user-defined conversion from ‘std::unique_ptr<AClass>’ to ‘const std::unique_ptr<const AClass>&’ [-fpermissive]
_

_unique_ptr<AClass>_から_unique_ptr<AClass const>_への変換は自動的に行われるべきではありませんか?ここで何が欠けていますか?

ちなみに、関数定義で_unique_ptr<AClass const> const & aPtr_を_unique_ptr<AClass> const & aPtr_に変更すると、コンパイルされますが、許可したくないaPtr->changeAClass()などの関数を呼び出すことができます。

22

スマートポインターは、所有権と有効期間を管理するためのものであり、(特に)コードのさまざまな部分で所有権を安全に転送できます。

const unique_ptr<T>&を関数に渡す場合(Tconstであるかどうかに関係なく)、実際に意味するのは、関数がunique_ptr自体を変更しないことを約束することです(ただし、 Tconst)でない場合に指し示されるオブジェクト、つまり所有権の譲渡は一切ありません。ネイキッドポインターの役に立たないラッパーとしてunique_ptrを使用しているだけです。

したがって、@ MarshallClowがコメントで提案したように、ラッパーを削除して、ネイキッドポインターまたは直接参照のいずれかを渡す必要があります。これのクールな点は、コードが意味的に明確になったことです(関数のシグネチャは、所有権を乱さないことを明確に示しています。これはではありませんconst unique_ptr<...>&)ですぐにわかり、同時に「構成」問題を解決します!

つまり:

void someFunction(const AClass* p) { ... }

std::unique_ptr<AClass> ptr(new AClass());
someFunction(ptr.get());

編集:二次的な質問に対処します "コンパイラーがなぜ許可しないのか... unique_ptr<A>unique_ptr<A const>にキャストしますか?"。

実際、次のことができます移動unique_ptr<A>からunique_ptr<A const>へ:

std::unique_ptr<A> p(new A());
std::unique_ptr<const A> q(std::move(p));

しかし、ご覧のとおり、これはpからqへの所有権の譲渡を意味します。

コードの問題は、unique_ptr<const A>(to to a function)(への参照)を渡していることです。 unique_ptr<A>には型の不一致があるため、これを機能させるには、コンパイラーが一時変数をインスタンス化する必要があります。ただし、std::moveを使用して手動で所有権を譲渡しない限り、コンパイラーはunique_ptrcopyしようとしますが、unique_ptrが明示的に禁止しているため、それを行うことはできません。

unique_ptrを移動すると、問題がどのように解消されるかに注意してください。

void test(const std::unique_ptr<const int>& p) { ... }

std::unique_ptr<int> p(new int(3));
test(std::move(p));

コンパイラーは、一時的なunique_ptr<const A>を作成し、期待を損なうことなく元のunique_ptr<A>を移動できるようになりました(コピーではなく移動したいことが明確になったため)。

したがって、問題の根本はunique_ptrに移動セマンティクスのみがあり、コピーセマンティクスはないことですが、一時的なandを作成するにはコピーセマンティクスが必要ですが、後で所有権は保持されます。 卵と鶏肉、unique_ptrはそのように設計されていません。

ここでshared_ptr whichhasコピーセマンティクスを検討すると、問題も解消されます。

void test(const std::shared_ptr<const int>& p) { ... }

std::shared_ptr<int> p(new int(3));
test(p);
//^^^^^ Works!

その理由は、コンパイラーが一時的なstd::shared_ptr<const int>copystd::shared_ptr<int>から自動的にキャスト)を作成し、その一時変数をconst参照にバインドできるようになったためです。

私の説明は標準的な専門用語に欠けていて、多分それがあるべきほど明確ではありませんが、これは多かれ少なかれそれをカバーしていると思います。 :)

32
syam

Const smart pointersに関するこの古い質問に行きました。上記の回答は、単純なテンプレートソリューションを無視しています。

非常にシンプルなテンプレートオプション(オプションa)

template<class T>
void foo(const unique_ptr<T>& ptr) {
    // do something with ptr
}

このソリューションでは、unique_ptrのすべての可能なオプションをfooに送信できます。

  1. const unique_ptr<const int>
  2. unique_ptr<const int>
  3. const unique_ptr<int>
  4. unique_ptr<int>

上記の3および4の理由で特に回避したい場合は、Tにconstを追加します。

Const/non-const unique_ptrのみをconstに受け入れます! (オプションb)

template<class T>
void foo(const unique_ptr<const T>& ptr) {
    // do something with ptr
}

サイドノート1

指摘された値を変更できる場合と変更できない場合で異なる動作をする場合は、「オプションa」と「オプションb」をオーバーロードすることができます。

サイドノート2

この関数のポイントされた値に変更を加えたくない場合(決して!取得するパラメーターのタイプは何でも!)-オーバーロードしないでください
「オプションb」を指定すると、コンパイラーは指定した値を変更できなくなります。仕事完了!
4つのケースすべてをサポートする場合、つまり「オプションa」の場合、関数はポイントする値を「誤って」変更する可能性があります。

template<class T>
void foo(const unique_ptr<T>& ptr) {
    *ptr = 3;
}

ただし、少なくとも1つの呼び出し元が実際にconstであるTを持っている場合、これは問題になりません。その場合、コンパイラーはそれを気に入らず、問題の解決に役立ちます。
このような発信者を単体テストで追加できます。

    foo(make_unique<const int>(7)); // if this line doesn't compile someone
                                    // is changing value inside foo which is
                                    // not allowed!
                                    // do not delete the test, fix foo!

コードスニペット: http://coliru.stacked-crooked.com/a/a36795cdf305d4c7

2
Amir Kirsh