クラステンプレート 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 はいいぞ