singletonについて

ググるとほとんどがCRTPを使用したものだった。

と、言うわけで、継承を使用せずに汎用的なsingletonを実現できないのかなぁと思って実際に作ってみた。

template <typename T>
class singleton : boost::noncopyable {
  singleton () {}
  ~singleton () {}
public:
  static T& get_instance () {
    static T t;
    return t;
  }
};

※訂正 2011/1/4 「struct singleton」じゃなくて「class singleton」デスヨネー

CRTPとの違いはコンストラクタとデストラクタがprivateな所だけ。
C++03用ならマルチスレッドに対応するため、適切にロックを掛ける必要があるけども気にしない気にしない。

使い方は簡単。

class tonimaru_ : boost::noncopyable {
  friend class singleton<tonimaru_>;
  tonimaru_ () {}
  ~tonimaru_ () {}
public:
  void print () {
    std::cout << "ようじょっ!\n";
  }
};
 
typedef singleton<tonimaru_> tonimaru;
 
int main() {
  tonimaru::get_instance().print();
}

出力結果:
ようじょっ!


完成。

と言っても、これじゃあ1個しか作れない。
たとえば、singletonの構造で最大5個まで作りたい時もあるはず。

そんなのに対応してみる。

template <typename T, size_t N = 1>
class singleton : boost::noncopyable {
  singleton () {}
  ~singleton () {}

public:
  template <size_t I>
  static T& get_instance () {
    BOOST_STATIC_ASSERT(I < N);
    return get_instance_(I);
  }

private:
  static T& get_instance_ (size_t i) {
    static T t[N];
    return t[i];
  }
};

template <typename T>
class singleton<T, 1u> : boost::noncopyable {
  singleton () {}
  ~singleton () {}

public:
  static T& get_instance () {
    static T t;
    return t;
  }
};

たぶん、これで大丈夫。
さっそく使ってみる。

class tonimaru_ : boost::noncopyable {
  friend class singleton<tonimaru_>;
  friend class singleton<tonimaru_, 2u>;
  tonimaru_ () {}
  ~tonimaru_ () {}
 
public:
  void print () {
    std::cout << "ようじょっ!\n";
  }
};
 
typedef singleton<tonimaru_> tonimaru;
typedef singleton<tonimaru_, 2> tonimaru_2;

これで定義は完了。
早速呼んでみる。

int main () {
  tonimaru::get_instance().print();
  tonimaru_2::get_instance<0>().print();
  tonimaru_2::get_instance<1>().print();
  tonimaru_2::get_instance<2>().print(); // えらー
}

うん、できた。
なんかびみょーだけど。
配列のサイズが大きいと最初の生成のコストが半端なかったり。
配列のサイズをクライアントが1に指定すると通常のsingletonになっちゃって若干直観的じゃない気もする。
まぁ今はこんなもんか。

※もちろん、なるべくsingletonを使用しない設計が大事ってのが大前提ですにゃ。