ヘッダーに次のようなコードがあります:
#include <memory>
class Thing;
class MyClass
{
std::unique_ptr< Thing > my_thing;
};
型定義を含まない cpp にこのヘッダーを含めるとThing
、VS2010-SP1 ではコンパイルされません。
1>C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\memory(2067): エラー C2027: 未定義の型 'Thing' が使用されています
std::unique_ptr
を に置き換えるstd::shared_ptr
とコンパイルされます。
std::unique_ptr
したがって、完全な定義を必要とするのは現在の VS2010 の実装であり、完全に実装に依存していると考えられます。
それともそうでしょうか? の標準要件に、std::unique_ptr
の実装が前方宣言のみで動作することを不可能にする何かがあるのでしょうか? は へのポインターのみを保持するはずなのでThing
、奇妙に感じませんか?
ベストアンサー1
採用元ここ。
C++標準ライブラリのほとんどのテンプレートは、完全な型でインスタンス化する必要があります。ただし、shared_ptr
とは部分的なunique_ptr
例外です。一部のメンバーは不完全な型でインスタンス化できますが、すべてではありません。これは、次のようなイディオムをサポートするためです。にきびスマート ポインターを使用し、未定義の動作のリスクを回避します。
不完全な型があり、delete
それを呼び出すと、未定義の動作が発生する可能性があります。
class A;
A* a = ...;
delete a;
上記は正当なコードです。コンパイルされます。コンパイラは、上記のようなコードに対して警告を出す場合と出さない場合があります。実行すると、おそらく悪いことが起こります。運が良ければ、プログラムがクラッシュします。しかし、より起こり得る結果は、プログラムが~A()
呼び出されないため、暗黙的にメモリ リークすることです。
auto_ptr<A>
上記の例でを使用することは役に立ちません。生のポインターを使用した場合と同じ未定義の動作が引き続き発生します。
それでも、特定の場所では不完全なクラスを使用することは非常に便利です。これがshared_ptr
と がunique_ptr
役立つところです。これらのスマート ポインターのいずれかを使用すると、完全な型が必要な場合を除き、不完全な型で済みます。そして最も重要なのは、完全な型が必要なときに、その時点で不完全な型でスマート ポインターを使用しようとすると、コンパイル時エラーが発生することです。
未定義の動作はなくなりました
コードがコンパイルされた場合、必要なすべての場所で完全な型が使用されています。
class A
{
class impl;
std::unique_ptr<impl> ptr_; // ok!
public:
A();
~A();
// ...
};
unique_ptr
およびの型完全性要件shared_ptr
shared_ptr
異なる場所で完全な型が必要になりunique_ptr
ます。理由は不明瞭で、動的削除子と静的削除子に関係しています。正確な理由は重要ではありません。実際、ほとんどのコードでは、完全な型が必要な場所を正確に知ることはそれほど重要ではありません。コードを記述するだけで、間違えた場合はコンパイラが教えてくれます。
ただし、役に立つかもしれないのでshared_ptr
、unique_ptr
完全性要件に関する と のいくつかの操作を文書化した表を以下に示します。
手術 | ユニークポインタ | 共有ptr |
---|---|---|
P() (デフォルトコンストラクタ) |
不完全な | 不完全な |
P(const P&) (コピーコンストラクター) |
— | 不完全な |
P(P&&) (コンストラクタの移動) |
不完全な | 不完全な |
~P() (デストラクタ) |
完了 | 不完全な |
P(A*) (ptr からのコンストラクタ) |
不完全な | 完了 |
operator=(const P&) (コピー割り当て) |
— | 不完全な |
operator=(P&&) (移動割り当て) |
完了 | 不完全な |
reset() |
完了 | 不完全な |
reset(A*) |
完了 | 完了 |
ポインタ変換を必要とする操作では、unique_ptr
との両方の完全な型が必要ですshared_ptr
。
コンストラクタは、コンパイラが の呼び出しを設定する必要がない場合にのみ、unique_ptr<A>{A*}
不完全な を回避できます。たとえば、 をヒープに置くと、不完全な を回避できます。この点の詳細については、を参照してください。A
~unique_ptr<A>()
unique_ptr
A
BarryTheHatchetの答えここ。