
Rob Stewart wrote:
From: Jason Hise <chaos@ezequal.com>
Rob Stewart wrote:
As I understand it, the operation is inherently unsafe; it isn't a matter of efficiency, or even desirable usage. Given that, it is most appropriate that you spell things differently. You want to call attention to such usage.
Even though the usage is undesirable, it may still be necessary in cases where the singleton needs to behave polymorphically like one of its base classes.
But you said that the object may not exist since it is operator ->() that does the lifetime management. Thus, the object may not actually exist when you call the unsafe function, so it is a dangerous operation. Did I misunderstand something?
operator-> is not solely responsible for lifetime management. Depending on the specific traits specified, automatic creation of the singleton instance can happen at various times. During a call to operator -> is just one of these times. There are explicit create and destroy member functions, and the singleton is generally scheduled to be automatically destroyed at one point or another. As a result, the automatic creation when operator -> is invoked is generally a last line of defense, which will recreate the instance if it has been destroyed (or in some rare circumstances will create the instance for the first time). So long as client code makes sure that the instance has already been created, that nothing destroys the instance while the raw pointer is in use, and that operations which need to be locked are locked manually, then that raw pointer is safe to use.
Having said that, is it really the case that operator *() couldn't do the same work as operator ->() (possibly adding an exception if you can't get the underlying object)?
The difference is that the pointer returned by operator -> is guaranteed to be used immediately, and by using an intermediate pointer class I can construct a lock for the duration of the member call (this technique is described in Modern C++ Design, in the smart pointer section).
So you're returning a proxy from operator ->(), right?
yes.
Operator * is incapable of constructing this type of temporary lock, and additionally there is no guarantee that the reference returned would used and discarded immediately. Once the actual reference is returned, no more checking can take place.
Why can't you return such a proxy from a member function like "get" or "get_object" and let that proxy convert to the singleton type?
That means you can spell "get_unsafe_ptr" without "unsafe" because the proxy ensures that it is safe (as safe as with operator ->(), anyway).
The action of getting the pointer itself is not unsafe. What is unsafe is maintaining that pointer and storing it for an extended period of time, during which the singleton could be destroyed and recreated. In almost every circumstance, it is preferable for client code to use and pass around singleton::pointers rather than singleton *s, just like it is preferable for client code to use smart pointers to manage memory rather than trying to manage memory and raw pointers manually. Managing them manually can be done safely, but it is harder and error prone. -Jason