🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

1 total messages Started by Greg Colvin Thu, 08 Feb 1996 18:10
auto_ptr idioms
#3331
Author: Greg Colvin
Date: Thu, 08 Feb 1996 18:10
103 lines
5704 bytes
Many of the comments I have received on auto_ptr seem to be based on a
desire to be able to use an auto_ptr "just like an ordinary pointer".
I share this desire, but I did not design auto_ptr to satisfy it.  My
gc_ptr and counted_ptr proposals came much closer, but did not satisfy
the committee.  Rather, auto_ptr was designed to provide exception safety in
just three very restricted idioms.  I hope a little motivation and history
will clarify the issues, and perhaps convince you of the need for the changes
I am proposing.

The fundamental problem for exception safety was fixing code like this:
   void g(T*);
   void f() {
      T* p(new T);
      g(p);
      delete p;
   }
If the function g() throws an exception then delete p will not be called.
Of course you could write this instead:
   void g(T*);
   void f() {
      T* p(new T);
      try {
         g(p);
      } catch(...) {
         delete p;
         throw;
      }
      delete p;
   }
But would you want to?  Not me.  The auto_ptr template provides a simpler
"resource acquistion is initialization" idiom:
   void g(T*);
   void f() {
      auto_ptr<T> p(new T);
      g(&*p);
   }
All that auto_ptr was absolutely *required* to do is support this idiom, and
that is all my original auto_ptr proposal did.  In this it was like earlier
sugestions by John Skaller and Steve Rumbsy.  However, Bill Gibbons of
Taligent reported: that Taligent had considerable experience with a class
much like auto_ptr, that they found that a copy semantics essential, and
that the appropriate semantics was to transfer ownership.

Given a copy semantics, two other useful idioms become possible:

   // sink() is responsible for deleting its argument
   void sink(auto_ptr<T>);

   // source() returns a pointer that its caller is responsible for deleting
   auto_ptr<T> source();

Given careful use of auto_ptr (no calls to get(), reset(), or release()) the
sink() function is sure to delete its argument, and the caller of source() is
sure to delete its returned pointer.  So far so good.

Unfortunately, the current semantics of C++ do not allow the following:
   auto_ptr<T> p(source());
This is because the copy semantics for auto_ptr are destructive (non-const),
but passing the value returned by source() to the copy constructor requires
a temporary, which cannot be passed to auto_ptr(auto_ptr&) for reasons that
remain obscure to me (and to all the compilers I own), but that are spelled
out in the ARM (8.4.3).

To fix the source() idiom I propose to make the auto_ptr copy semantics
non-destructive.  Currently, when you copy an auto_ptr the source ends up
holding a null pointer.  I propose instead to leave the source pointer
unchanged, but transfer ownership of the object pointed to.  Arguably, a
change in ownership is not a change in the state of any particular auto_ptr,
and no public function will tell you which auto_ptr owns a particular pointer,
so the copy constructor and assignment operator are "logically" const, and we
can use mutable state to implement them with const arguments.

Note that this change does not affect the three idioms above at all: you
would write the same code after the change as you would now.  Still, the
change is not for free.  Some criticisms and my response follow.

Performance.  A little more space and time will be needed to represent the
pointer value and the object ownership separately.
   True, but the cost is small even for portable implementations, and can be
   made even smaller in non-portable ones.  I think the cost is worth it.

Safety.  Now, an auto_ptr cannot be a dangling pointer, although it can be
null.  With this change a non-null auto_ptr might be a dangling pointer.
   True, but this is not a problem for the idioms auto_ptr is designed to
   support.  The problem was to be sure that pointers held in automatic
   variables are deleted when exceptions are thrown, not to prevent dangling
   pointers.  Regardless, dereferencing a null pointer is no less undefined
   than dereferencing a dangling pointer, and the available memory utilities
   like Purify, BoundsChecker, and Great Circle will handle either case
   equally well.

Convenience.  Now, I can use get() to test whether an auto_ptr is still
valid.  With this change I cannot.
   True, but again not a problem for the above idioms.  In my opinion, the
   effective use of auto_ptr requires careful design.  Questions like whether
   an auto_ptr is an owner or is valid must be answerable statically, by
   design, any attempt to answer them at runtime will be fraught with peril.
   If you really need to answer these questions at runtime then you need
   some form of reference counting or tracing garbage collection.
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy is
  in http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads