0

I have a template class that implements a singleton:

template<typename T>
class Singleton {
     static Singleton const& get() const { return _instance; }
   private:
      Singleton();
      static Singleton _instance;
};

There are some specializations of this template, for example, for Type1 and Type2. And its constructors there are calls of get() of each other. I see a problem. Let's assume that Singleton of Type1 is constructed at first:

Singleton<Type1>::Singleton() {
   Singletion<Type2>::get();
}

So the Singleton of Type2 is not constructed yet. What will be returned by get()?

jogojapan
  • 68,383
  • 11
  • 101
  • 131
user14416
  • 2,922
  • 5
  • 40
  • 67

2 Answers2

1

You need to make the _instance a static inside the get function.

template<typename T>
class Singleton {
     static Singleton const& get() const {
       static Singleton _instance;
       return _instance;
     }
   private:
      Singleton();
};

This way, the instance will be initialized the first time get is called.

Pubby
  • 51,882
  • 13
  • 139
  • 180
  • yeah. Good idea. But what happens next? `Singleton::Singleton()` calls the execution of `Singleton::Singleton()` which need `static` `_instance` variable of `Singleton` which is in construction. – user14416 Mar 04 '13 at 02:28
  • @user14416 Probably undefined behavior. This is a circular dependency and surely a mistake. – Pubby Mar 04 '13 at 02:37
  • Your idea does not fit me. I need these singleton initialization during the program start up. – user14416 Mar 04 '13 at 02:38
  • @user14416 You should instead ask about what you're trying to achieve. Why do you need 2 singletons with circular dependencies? – Pubby Mar 04 '13 at 02:40
  • Don't be so sure. I need to know what happens in my original question? Do you know? – user14416 Mar 04 '13 at 02:40
  • That's an issue, they will invoke infinite recursion. Gcc would result in '__gnu_cxx::recursive_init_error' – sehe Mar 04 '13 at 02:41
  • @user14416 in your original code you would be accessing something that didn't exist yet. Undefined behavior. – Pubby Mar 04 '13 at 02:42
  • @Pubby you won't be accessing it: static initializers will deadlock, nothing will happen. – sehe Mar 04 '13 at 02:43
  • @user14416 See my answer for a deconstruction of your cyclic dependency. – sehe Mar 04 '13 at 02:49
0

The cyclic dependency is bad:

struct Singleton2;

struct Singleton1 {
     static Singleton1 const& get() {
       static Singleton1 _instance;
       return _instance;
     }
   private:
      Singleton1();
      Singleton2 const& _s2;
};

struct Singleton2 {
     static Singleton2 const& get() {
       static Singleton2 _instance;
       return _instance;
     }
   private:
      Singleton2();
      Singleton1 const& _s1;
};

Singleton1::Singleton1() : _s2(Singleton2::get()) { }
Singleton2::Singleton2() : _s1(Singleton1::get()) { }

int main()
{
    auto& s1 = Singleton1::get();
    auto& s2 = Singleton2::get();
}

Will result in a failure (see http://liveworkspace.org/code/4rPFDo$0).

Like in all cases where you need to break cycles, make at least one link in the chain lazy.

Solve it in the obvious way by not requiring a reference to the other singleton during construction:

struct Singleton2;

struct Singleton1 {
     static Singleton1 const& get() {
       static Singleton1 _instance;
       return _instance;
     }
   private:
      Singleton1() {}
      static Singleton2 const& getOther();
};

struct Singleton2 {
     static Singleton2 const& get() {
       static Singleton2 _instance;
       return _instance;
     }
   private:
      Singleton2() {}
      static Singleton1 const& getOther();
};

Singleton2 const& Singleton1::getOther() { return Singleton2::get(); }
Singleton1 const& Singleton2::getOther() { return Singleton1::get(); }

int main()
{
    auto& s1 = Singleton1::get();
    auto& s2 = Singleton2::get();
}

Alternatively delay-initialize using boost::optional, boost::flyweight or a custom 'lazy_ptr': https://stackoverflow.com/a/878298/85371

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633