28

ヘルパー関数に aを渡したいのですが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()

4

2 に答える 2

40

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

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

したがって、@MarshallClow がコメントで示唆したように、ラッパーを取り除き、ネイキッド ポインターまたは直接参照を渡す必要があります。これで素晴らしいのは、コードが意味的に明確になり(関数の署名は、所有権を台無しにしないことを明確に示していますが、これはa ではすぐには明らかではありませんでした)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>か?」.

実際には、 aを a に移動できます:unique_ptr<A>unique_ptr<A const>

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

しかし、ご覧のとおり、これは から への所有権の移転を意味pqます。

unique_ptr<const A> コードの問題は、(への参照) を functionに渡していることです。には型の不一致があるunique_ptr<A>ため、それを機能させるには、コンパイラがテンポラリをインスタンス化する必要があります。ただし、 を使用して手動で所有権を譲渡しない限りstd::move、コンパイラはコピーを試みますが、明示的に禁止されているためコピーunique_ptrできません。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するには、コピー セマンティクスが必要です。卵と鶏肉は、unique_ptrそのように設計されていません.

shared_ptrどちらコピー セマンティクスを持っているかを考えると、問題も解消されます。

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> コピーを作成し(から自動的にキャストstd::shared_ptr<int>)、その一時コピーを参照にバインドできるようになったためconstです。

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

于 2013-05-07T18:06:51.620 に答える
2

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

非常に単純なテンプレート オプション (オプション 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 には const/non-const の unique_ptr のみを受け入れます! (オプション 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

于 2016-02-15T22:30:05.280 に答える