クラステンプレート std::enable_shared_from_this
とは、std::shared_ptr
で所有権を管理している場合、これを継承するとインスタンス自身から shared_ptr
を逆引きすることができる(shared_from_this()
メンバ関数を基底クラスに持つ)という便利なテンプレートです。
なぜこのようなものが必要になるのかは、下のオージス総研さんの記事が詳しいです(2009年の記事なので boost ライブラリの説明ですが、内容は後に追加された標準ライブラリにも通用します)。
さて、このテンプレートは C++11 で shared_ptr
と共に追加されたのですが、C++17 で微妙ながら重要な変更があることに気づいたのでご紹介します。
C++11
shared_ptr
を逆引きするという性質上、当然ながら shared_ptr
で所有権を管理されていないインスタンスからは取得することができません。
ここまではいいのですが、C++11 では shared_from_this()
関数の制約として、shared_ptr
で所有権を管理されていることを「必須 (Requirements)」としました。
これが何を意味するかと言うと、shared_ptr
で管理されていない場合、shared_from_this()
の動作は 未定義動作 であるということです。
それにも関わらず、enable_shared_from_this
には、今現在 shared_ptr
で管理されているかどうかを判別する方法が存在しません。
この2つから、enable_shared_from_this
は少し使い方を誤っただけで容易に未定義動作を引き起こしうるという問題が存在しました。
C++17
これが C++17 で以下のように変わりました。
-
shared_ptr
で管理されていることが「必須」ではなくなった -
enable_shared_from_this
に新しいメンバ関数weak_from_this()
が追加され、さらにメンバ変数std::weak_ptr<T> weak_this
があることが明示された -
shared_ptr
のコンストラクタでenable_shared_from_this::weak_this
に参照を格納することが明示された -
shared_from_this()
の戻り値は、weak_this
をshared_ptr
に変換したものであることが明示された
この仕様変更によって、以下のように状況が変わりました。
-
weak_from_this()
の戻り値を使って、今現在shared_ptr
で管理されているかどうかを判別することができるようになった(weak_ptr::use_count()
やexpired()
などを使えばよい) -
shared_ptr
で管理されていないインスタンスのshared_from_this()
を呼んだ場合、std::bad_weak_ptr
例外が(未代入のweak_ptr
をshared_ptr
に変換したときに)発生することが保証され、未定義動作ではなくなった
結論
C++17 はいいぞ