const版シングルトン

前回の記事にconst参照版があった方がいいという突っ込みを受けたのでBoost.Serializationのシングルトンを参考にして改良しました.

template <class T>
class Singleton {
public:
  virtual void init() = 0;
  virtual void exec() = 0;
  virtual void term() = 0;

  static T& getMutable() { return getIns(); }
  static const T& getConst() { return getIns(); }

protected:
  Singleton() {}
  virtual ~Singleton() {}

private:
  static void use(const T&) {}
  static T& getIns() {
    static T ins;
    use(ins);
    return ins;
  }

  Singleton(const Singleton& rhs) {}
  void operator=(const Singleton& rhs) {}
};

class GameSgt : public Singleton<GameSgt> {
public:
  void init() {
    std::cout << "mutable init()" << std::endl;
  }
  void exec() {
    std::cout << "mutable exec()" << std::endl;
  }
  void term() {
    std::cout << "mutable term()" << std::endl;
  }

  void init() const {
    std::cout << "const init()" << std::endl;
  }
  void exec() const {
    std::cout << "const exec()" << std::endl;
  }
  void term() const {
    std::cout << "const term()" << std::endl;
  }

private:
  friend class Singleton<GameSgt>;
  GameSgt() {}
};


前回との違いはget()と同じ機能を持つgetConst()とgetMutable()です.通常local static変数はそのまま返してもconstがつきませんが,use()にconst T&として渡す事によりlocal static変数をconstとして扱えるようになります.あとはアクセサとしてgetMutable()とgetConst()を用意し,当然const参照版はconstによりimmutableなオブジェクトとして扱えるのでマルチスレッド環境でも安全な呼び出しを行えるはず.実験はしていないので保証は出来ませんが.実際に以下のように実行します.

int main() {
  GameSgt::getMutable().init();
  GameSgt::getMutable().exec();
  GameSgt::getMutable().term();

  GameSgt::getConst().init();
  GameSgt::getConst().exec();
  GameSgt::getConst().term();
}
mutable init()
mutable exec()
mutable term()
const init()
const exec()
const term()


という感じでちゃんと呼びわけが出来ているのがわかります.当然,const版はconstメンバしか呼び出せませんが.上手く活用すればより安全なシングルトンとして使用出来るのではないかと思います.

■追記

改めてコードを見直してみると,use()がなくても普通にコンパイル通りますね…なんか勘違いをしていたようです.まだまだ勉強不足…Boost.Serializationの場合は完全にmain()が実行される前にインスタンス化が行なわれるようですが,この場合どのインスタンスが順番に実体化するかわからなくってしまう…