yohhoyの日記

技術的メモをしていきたい日記

enable_shared_from_thisクラステンプレート

C++11標準ライブラリで追加されたenable_shared_from_thisクラステンプレートについてメモ。Boost.Smart Pointersライブラリでも同機能を提供している。

N3337 20.7.2.4/p1より引用。

A class T can inherit from enable_shared_from_this<T> to inherit the shared_from_this member functions that obtain a shared_ptr instance pointing to *this.

具体的な利用例としては http://www.boost.org/doc/html/boost_asio/tutorial/tutdaytime3.html など(使用理由の説明)。

正しい利用方法

「クラス型T*thisへのshared_ptr<T>を取得」を実装するためのヘルパであり、対象クラスに対し CRTP(Curiously Recurring Template Pattern) の形で利用する。またenable_shared_from_thisはpublic継承とする必要がある*1

#include <memory>

struct X : public std::enable_shared_from_this<X> {
  void func() {
    // 自分自身(*this)へのshared_ptr<X>型を取り出し
    auto self = shared_from_this();
    //...
  }
};

auto ptr = std::make_shared<X>(/*...*/);
/*...*/ = ptr->func();

この例では、X::func()で取り出したshared_ptrを同メンバ関数の外で引き続き保持することを仮定している。(X::funcメンバ関数内でしかthisを使わないのであれば、そもそもshared_ptrとして取り出す必然性が無い。)

誤った利用方法(1)

本クラステンプレートの利用にあたっては、オブジェクト自身(*this)がshared_ptrで所有権管理されていることが前提となる。

struct X : public std::enable_shared_from_this<X> {
  void func() {
    auto self = shared_from_this();  // (2) NG: Requiresを満たさない
  }
};

auto ptr = new X(/*...*/);  // (1) shared_ptr管理でない場合...
ptr->func();

上記コードのようにshared_from_thisメンバ関数の要件を満たさない場合の挙動は、C++規格では明確に定義されていない。なお、gcc 4.6.3ではstd::bad_weak_ptr例外が送出された。(後述の内部実装がされていれば、他処理系でも同例外が送出される可能性あり。)

誤った利用方法(2)

struct Y {
  void func() {
    auto self = std:shared_ptr<Y>(this);  // NG!!
  }
};

auto ptr = std::make_shared<Y>(/*...*/);
ptr->func();

上記コードのようにthisから直接shared_ptrを作成した場合、selfptrで連携がとれず所有権管理が正常に機能しない。(この例では変数selfのデストラクタで意図しないY::~Y()が呼びだされる。)

// 前述コードの問題点を単純化したコード
Y *obj = new Y;
std::shared_ptr<Y> ptr(obj);
std::shared_ptr<Y> self(obj);  // NG!!

内部実装の例

enable_shared_from_thisクラステンプレートが提供する機能は「*thisへのweak_ptrを保持(A)しておき、shared_from_thisメンバ関数shared_ptrへ変換(昇格)する」だけ。またshared_ptrコンストラクタでは、“enable_shared_from_thisから派生したクラスであること” を検出して前述(A)*thisへのweak_ptrを適切に設定する。実装例をN3337 20.7.2.4/p10-11より引用*2

10 [ Note: A possible implementation is shown below:

template<class T> class enable_shared_from_this {
private:
  weak_ptr<T> __weak_this;
protected:
  constexpr enable_shared_from_this() : __weak_this() { }
  enable_shared_from_this(enable_shared_from_this const &) { }
  enable_shared_from_this& operator=(enable_shared_from_this const &) { return *this; }
  ~enable_shared_from_this() { }
public:
  shared_ptr<T> shared_from_this() { return shared_ptr<T>(__weak_this); }
  shared_ptr<T const> shared_from_this() const { return shared_ptr<T const>(__weak_this); }
};

11 The shared_ptr constructors that create unique pointers can detect the presence of an enable_shared_from_this base and assign the newly created shared_ptr to its __weak_this member. -- end note ]

関連URL

*1:N3337 20.7.2.4/p7 "Requires: enable_shared_from_this shall be an accessible base class of T."

*2:boost::enable_shared_from_this の実装:http://www.boost.org/boost/smart_ptr/enable_shared_from_this.hpp