
DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=beta; d=gmail.com; h=received:message-id:date:from:reply-to:to:subject:in-reply-to:mime-version:content-type:content-transfer-encoding:references; b=MZ2/STCvbocBJRKdRXXSdw8PEyGA45EQ1ZT81RfX2GtTF/tK2gXcyhr9okaGWB5dxhggJkBvr8eZCj4/GF+w/HM25bcs9e6kJI5arrFDY7nZi8UV747ffQUJBAUo/TykzCLAUq5VbtTDIuXp2khe/tV/B5NiFg7Wnz4ag9A2+FM= From: Dan Rosen <dan.rosen@gmail.com>
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.
Let me address a couple of points raised. First, assuming what you've outlined is an acceptable approach, what you've called noninstantiable should be called singleton_base so that clients derive from singleton_base. (Adjust the other names similarly.) The next point is whether requiring private ctor/dtor is better than using a noninstantiable scheme (not necessarily the one shown above). The OP's goal is to prevent instantiation of a type as both a Singleton and otherwise. If you accept that goal, then you want to prevent mistakes and a noninstantiable scheme is needed. Otherwise, it is easy to forget to Do The Right Thing (tm) and ensure your class can't be both a Singleton and not. I prefer to have the compiler catch mistakes and, if you accept the stated goal, this is a mistake that the compiler can catch. The question is whether the goal is acceptable. Consider a printer Singleton. There may be one, default printer, and that can be captured quite handily via a Singleton. The question is: Can there be other printers? Obviously, there can, so preventing instantiation of printer outside the Singleton framework is overkill. I contend that you can provide a mechanism whereby clients can ensure their type can only be instantiated by the Singleton framework, but don't require that. IOW, singleton_base could be used to prevent non-Singleton instantiation, but make its use optional. (Making it optional also prevents MI problems for types that already derive from other classes.) Thus, I think the framework should permit singleton<A> and singleton<B> (where noninstantiable is called singleton_base). -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;