このプログラムを考えてみましょう:
#include <memory>
#include <iostream>
class X
: public std::enable_shared_from_this<X>
{
public:
struct Cleanup1 { void operator()(X*) const; };
struct Cleanup2 { void operator()(X*) const; };
std::shared_ptr<X> lock1();
std::shared_ptr<X> lock2();
};
std::shared_ptr<X> X::lock1()
{
std::cout << "Resource 1 locked" << std::endl;
return std::shared_ptr<X>(this, Cleanup1());
}
std::shared_ptr<X> X::lock2()
{
std::cout << "Resource 2 locked" << std::endl;
return std::shared_ptr<X>(this, Cleanup2());
}
void X::Cleanup1::operator()(X*) const
{
std::cout << "Resource 1 unlocked" << std::endl;
}
void X::Cleanup2::operator()(X*) const
{
std::cout << "Resource 2 unlocked" << std::endl;
}
int main()
{
std::cout << std::boolalpha;
X x;
std::shared_ptr<X> p1 = x.lock1();
{
std::shared_ptr<X> p2 = x.lock2();
}
}
C++11 標準セクション 20.7.2 には、これが無効であることを示唆するものは何もありません。2 つのshared_ptr
オブジェクトが同じポインタを格納している&x
が所有権を共有していないこと、および の存続期間を終了させない「deleter」を使用することは少し珍しいことです*get()
が、それを禁止するものは何もありません。shared_ptr
(そして、それらのいずれかがまったく意図されていない場合、一部のメンバー関数が値を受け入れる理由を説明するのは困難std::nullptr_t
です。) そして、予想どおり、プログラムは次のように出力します。
Resource 1 locked
Resource 2 locked
Resource 2 unlocked
Resource 1 unlocked
しかし、今、少し追加するとmain()
:
int main()
{
std::cout << std::boolalpha;
X x;
std::shared_ptr<X> p1 = x.lock1();
bool test1( x.shared_from_this() );
std::cout << "x.shared_from_this() not empty: " << test1 << std::endl;
{
std::shared_ptr<X> p2 = x.lock2();
}
try {
bool test2( x.shared_from_this() );
std::cout << "x.shared_from_this() not empty: " << test2 << std::endl;
} catch (std::exception& e) {
std::cout << "caught: " << e.what() << std::endl;
}
}
その後、事態はさらに複雑になります。g++ 4.6.3 では、次の出力が得られます。
Resource 1 locked
x.shared_from_this() not empty: true
Resource 2 locked
Resource 2 unlocked
caught: std::bad_weak_ptr
Resource 1 unlocked
2 番目の呼び出しがshared_from_this()
失敗するのはなぜですか? 20.7.2.4p7 のすべての要件が満たされています。
Requires:
enable_shared_from_this<T>
のアクセス可能な基本クラスでなければなりませんT
。タイプ*this
のオブジェクトのサブオブジェクトでなければなりません。を所有するインスタンスが少なくとも 1 つ存在する必要があります。t
T
shared_ptr
p
&t
【T
はX
、t
はx
、p
はp1
。】
しかし、g++enable_shared_from_this
は基本的に、20.7.2.4p10 の (非規範的な) 「注」から提案された実装に従い、weak_ptr
クラスのプライベート メンバーを使用しenable_shared_from_this
ます。そして、 でかなり複雑なことをしなければ、この種の問題を説明することは不可能に思えますenable_shared_from_this
。
これは規格の欠陥ですか? (そうであれば、解決策が「あるべき」ものについてここでコメントする必要はありません。サンプルプログラムが未定義の動作を呼び出すように要件を追加し、そのような単純な実装で十分であることを示唆しないように注を変更します....)