_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()
などの関数を呼び出すことができます。
スマートポインターは、所有権と有効期間を管理するためのものであり、(特に)コードのさまざまな部分で所有権を安全に転送できます。
const unique_ptr<T>&
を関数に渡す場合(T
がconst
であるかどうかに関係なく)、実際に意味するのは、関数がunique_ptr
自体を変更しないことを約束することです(ただし、 T
がconst
)でない場合に指し示されるオブジェクト、つまり所有権の譲渡は一切ありません。ネイキッドポインターの役に立たないラッパーとして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_ptr
をcopyしようとしますが、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>
copy(std::shared_ptr<int>
から自動的にキャスト)を作成し、その一時変数をconst
参照にバインドできるようになったためです。
私の説明は標準的な専門用語に欠けていて、多分それがあるべきほど明確ではありませんが、これは多かれ少なかれそれをカバーしていると思います。 :)
Const smart pointersに関するこの古い質問に行きました。上記の回答は、単純なテンプレートソリューションを無視しています。
template<class T>
void foo(const unique_ptr<T>& ptr) {
// do something with ptr
}
このソリューションでは、unique_ptrのすべての可能なオプションをfooに送信できます。
const unique_ptr<const int>
unique_ptr<const int>
const unique_ptr<int>
unique_ptr<int>
上記の3および4の理由で特に回避したい場合は、Tにconstを追加します。
template<class T>
void foo(const unique_ptr<const T>& ptr) {
// do something with ptr
}
指摘された値を変更できる場合と変更できない場合で異なる動作をする場合は、「オプションa」と「オプションb」をオーバーロードすることができます。
この関数のポイントされた値に変更を加えたくない場合(決して!取得するパラメーターのタイプは何でも!)-オーバーロードしないでください 。
「オプション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