boost::enable_shared_from_this

boost::shared_ptr を使ってると時々問題になるのが、this の扱い。this は生のポインタなので出来る限り触りたくない、けれどもどうしても this が必要になる場合があったりする。例えばダブルディスパッチとか。
そういった this のスマートポインタが欲しい場合、enable_shared_from_this を使うことによって解決することができる。

#include <boost/enable_shared_from_this.hpp>
#include <string>
#include <iostream>

class Hoge
{
private:
    std::string str_;

public:
    Hoge(const std::string& str) : str_(str) { }

    template<class T>
    void Foo(const boost::shared_ptr<T>& obj) const
    {
        std::cout << obj->name() << ": " << str_ << std::endl;
    }
};

class Fuga : public boost::enable_shared_from_this<Fuga>
{
public:
    std::string name() const { return "Fuga"; }

    void Foo(boost::shared_ptr<Hoge> hoge) const
    {
        hoge->Foo(shared_from_this());
    }
};

void main()
{
    boost::shared_ptr<Hoge> hoge(new Hoge("aaaaa"));
    boost::shared_ptr<Fuga>(new Fuga())->Foo(hoge);
}

実際のところ、↑の例はポインタでも運用できるからあまりいい例じゃないんだけど、うまい例が見つからなかった。


で、boost::enable_shared_from_this にも弱点があって、当然ながらそのクラスのコンストラクタで shared_from_this を使うことは出来ない。

class Piyo : public boost::enable_shared_from_this<Piyo>
{
public:
    Piyo(std::vector<boost::shared_ptr<Piyo> >& v)
    {
        v.push_back(shared_from_this()); // これは間違い
    }
};

void main()
{
    std::vector<boost::shared_ptr<Piyo> > v;
    boost::shared_ptr<Piyo> piyo(new Piyo(v));
}

boost::shared_ptr にセットされるときに shared_from_this は有効になるのだけれども、new Piyo() をした段階ではまだ boost::shared_ptr にセットされていないので、コンストラクタで取得しようとすると実行時エラー(アサートに引っかかる)になってしまう。


しばらく頑張ってみたけどあまりいい方法が見つからなかったので、とりあえず二段階初期化を行う関数を用意して何とかしました。