From: Dan Rosen (dan.rosen_at_[hidden])
Date: 2005-03-25 21:21:01


> All that said, there's still a flaw in your approach, which I've
> inherited in mine. That is: neither of our singleton<> class templates
> actually force clients to derive from their intended bases
> (singleton_base in your case, and noninstantiable in mine).

Okay, I figured out a way to make this work. It's terrible and I hate
it, and I think the simple approach (private ctor/dtor) is best, but I
feel like I have to post my solution for geeky completeness. Anyway,
here it is:

  template <typename T> class noninstantiable {
  protected:
    virtual void myob () = 0;
  };
  template <typename T> void noninstantiable<T>::myob () { }

  template <typename T> class instantiable : public T {
  private:
    virtual void myob () { this->noninstantiable<T>::myob(); }
  };

If this isn't self-explanatory, what's going on here is that
noninstantiable<> is basically the same as before, but instantiable<>
forces its clients to derive from noninstantiable by referring to its
myob() member directly. So there are four scenarios:

  class A { };
  class B : public noninstantiable<B> { };
  typedef instantiable<A> C;
  typedef instantiable<B> D;

  A a; // fine
  B b; // error: myob not implemented
  C c; // error: doesn't derive from noninstantiable
  D d; // fine

This still doesn't allow clients to write literally zero code, but it
does close the loophole of case C above. Again, though, you'll have a
hard time convincing me that this is even marginally better than just
having a private ctor/dtor and explicitly declaring the friends who
you want to allow instantiation privileges to. This approach is less
safe, has more overhead, and is arcane.

dr