の 構築中に (明確に定義された動作で) ポインターを削除できる限り、 を使用して不完全な型へのポインターを格納できることは広く 知られています。たとえば、PIMPL 手法は次のとおりです。shared_ptr
shared_ptr
struct interface
{
interface(); // out-of-line definition required
~interface() = default; // public inline member, even if implicitly defined
void foo();
private:
struct impl; // incomplete type
std::shared_ptr<impl> pimpl; // pointer to incomplete type
};
[main.cpp]
int main()
{
interface i;
i.foo();
}
[インターフェイス.cpp]
struct interface::impl
{
void foo()
{
std::cout << "woof!\n";
}
};
interface::interface()
: pimpl( new impl ) // `delete impl` is well-formed at this point
{}
void interface::foo()
{
pimpl->foo();
}
これは、「削除オブジェクト」「所有者オブジェクト」(*) として機能し、 の構築中に作成されshared_ptr
、pimpl( new impl )
型消去後に に格納されshared_ptr
ます。この「所有者オブジェクト」は、指定されたオブジェクトを破棄するために後で使用されます。そのため、 のインラインデストラクタを提供しても安全ですinterface
。
質問:規格は安全であることをどこで保証していますか?
(*) 標準の点ではデリータではありません。以下を参照してください。ただし、カスタムのデリータを呼び出すか、削除式を呼び出します。このオブジェクトは通常、ブックキーピング オブジェクトの一部として格納され、型の消去を適用し、仮想関数でカスタムの削除子/削除式を呼び出します。この時点で、削除式も適切な形式になっているはずです。
github リポジトリの最新ドラフト (94c8fc71、N3797 を改訂) を参照すると、[util.smartptr.shared.const]
template<class Y> explicit shared_ptr(Y* p);
3 必須:
p
に変換可能である必要がありますT*
。Y
完全型になります。式delete p
は整形式でなければならず、動作が明確に定義されていなければならず、例外をスローしてはなりません。
shared_ptr
4 効果:ポインターを所有するオブジェクトを構築しますp
。5 事後条件:
use_count() == 1 && get() == p
.6 スロー:
bad_alloc
、またはメモリ以外のリソースを取得できなかった場合の実装定義の例外。
注:このshared_ptr
ctor の場合、 は deleter を所有する必要はありません。deleterによって、標準はカスタム deleterを意味するようです。たとえば、構築中に追加のパラメーターとして提供します (または、コピー割り当てなどによりshared_ptr
、別のものを取得/共有します)。shared_ptr
参照してください ([util.smartptr.shared.const]/9 も参照)。実装 (boost、libstdc++、MSVC、およびすべての正気な実装だと思います) は、常に「所有者オブジェクト」を格納します。
deleterはカスタム deleterであるため、カスタム deleter がない場合、 のデストラクタは(delete-expression)shared_ptr
に関して定義されます。delete
[util.smartptr.shared.dest]
~shared_ptr();
1 効果:
*this
が空shared_ptr
であるか、別のインスタンス ( )と所有権を共有している場合use_count() > 1
、副作用はありません。- それ以外の場合、がオブジェクトを
*this
所有し、p
deleter が呼び出される場合。d
d(p)
- それ以外の場合は、ポインターを
*this
所有し、呼び出されます。p
delete p
意図shared_ptr
は、 dtorのスコープ内で削除式が不適切な形式であるか、UB を呼び出す場合でも、格納されたポインターを正しく削除するために実装が必要であるということであると仮定します。(delete-expression は整形式であり、ctor で明確に定義された動作を持っている必要があります。) したがって、問題は次のとおりです。
質問:これはどこで必要ですか?
(または、私はあまりにもうるさいので、実装が「所有者オブジェクト」を使用する必要があることはどういうわけか明らかですか?)