Topic: auto_ptr + mutable + map (was Something I don't understand about auto_ptr)


Author: James.Kanze@dresdner-bank.com
Date: 1999/03/19
Raw View
In article <921598274.15573.0.nnrp-07.9e981003@news.demon.co.uk>,
  "Ben Jones" <ben_j@usa.net> wrote:
> Sorry, coming in late on this discussion!...
>
> >You can't legally do it with vector, either.  Part of the reason why the
> >standard auto_ptr is so complicated is precisely to allow returning it
> >from a function, but to disallow its use in the standard containers.
> >The semantics of the earlier "copy" constructor were not copy, and do
> >not support use in a standard container.  Any standard container.
>
> In the MSVC implementation of the STL, auto_ptr does have a copy constructor
> which allows it to be used in a standard container:
>     auto_ptr(const auto_ptr<T>& rhs) throw();
>
> Do I take it from your comments that this is incorrect and non-portable?

The exact specifications for auto_ptr were one of the last things to
become frozen in the standard.  And earlier versions of auto_ptr did
have a copy constructor.  One which took a non-const reference as
argument.

The reason for the change is that the non-const reference didn't allow
returning an auto_ptr from a function.  One of the possible work-arounds
was to declare the copy constructor to take a const reference, then cast
away const in the constructor.

Even when the syntax allows it, instantiating a standard container over
auto_ptr is not legal, since the copy constructor does *not* have true
copy semantics.  A standard container is allowed to make as many copies
as it wishes, and the copy constructor for auto_ptr (when it exists),
modifies the object being copied.

> Try the following -- it works fine on MSVC:
>
> class MYCLASS
> {
> public:
>   MYCLASS(int i) : v(i) { cout << "creating " << v << endl; }
>   ~MYCLASS() { cout << "destroying " << v << endl; }
>   int Value() { return v; }
> private:
>   int v;
> };
>
> main()
> {
>   std::vector< std::auto_ptr<MYCLASS> > vec;
>   std::auto_ptr<MYCLASS> p(new MYCLASS(10));
>   vec.push_back(p); // vec takes ownership
>   std::vector< std::auto_ptr<MYCLASS> > vec2 = vec; // transfer into vec2
>   std::auto_ptr<MYCLASS> p2 = vec2[0]; // transfer into p2
>   cout << p2->Value() << endl;
>   return 0;
> }

In simple cases, for simple containers, there is a chance that the code
will work.  It is never guaranteed, and I think even in the MSVC
implementation, however, that a sort on a vector< auto_ptr< T > > will
break.

--
James Kanze                         mailto: James.Kanze@dresdner-bank.com
Conseils en informatique orient   e objet/
                              Beratung in industrieller Datenverarbeitung
Ziegelh   ttenweg 17a, 60598 Frankfurt, Germany    Tel. +49 (069) 263 17946

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Ben Jones" <ben_j@usa.net>
Date: 1999/03/16
Raw View
Sorry, coming in late on this discussion!...

>You can't legally do it with vector, either.  Part of the reason why the
>standard auto_ptr is so complicated is precisely to allow returning it
>from a function, but to disallow its use in the standard containers.
>The semantics of the earlier "copy" constructor were not copy, and do
>not support use in a standard container.  Any standard container.


In the MSVC implementation of the STL, auto_ptr does have a copy constructor
which allows it to be used in a standard container:
    auto_ptr(const auto_ptr<T>& rhs) throw();

Do I take it from your comments that this is incorrect and non-portable?

Try the following -- it works fine on MSVC:

class MYCLASS
{
public:
  MYCLASS(int i) : v(i) { cout << "creating " << v << endl; }
  ~MYCLASS() { cout << "destroying " << v << endl; }
  int Value() { return v; }
private:
  int v;
};

main()
{
  std::vector< std::auto_ptr<MYCLASS> > vec;
  std::auto_ptr<MYCLASS> p(new MYCLASS(10));
  vec.push_back(p); // vec takes ownership
  std::vector< std::auto_ptr<MYCLASS> > vec2 = vec; // transfer into vec2
  std::auto_ptr<MYCLASS> p2 = vec2[0]; // transfer into p2
  cout << p2->Value() << endl;
  return 0;
}
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Matt Austern <austern@sgi.com>
Date: 1999/03/17
Raw View
"Ben Jones" <ben_j@usa.net> writes:

> This means it is possible to have an auto_ptr in a standard container, and
> ownership of the object is transferred correctly. But James Kanze wrote
> earlier in this thread:
> > Part of the reason why the
> > standard auto_ptr is so complicated is precisely to allow returning it
> > from a function, but to disallow its use in the standard containers.
>
> So is the MSVC implementation "non-standard"?

That's a slightly harsh way of putting it.  The definition of auto_ptr
changed several times during the standardization process.  Microsoft
shipped their library with an auto_ptr implementation based on an
early draft of the standard, in which auto_ptr had a copy constructor
    auto_ptr(const auto_ptr&).
By the time the standard became final, however, auto_ptr changed; its
"copy constructor" (scare quotes because this member function performs
a transfer operation, not a copy operation) is now
    auto_ptr(auto_ptr&).
If the Microsoft implementation is using the earlier version, then
presumably it is deliberately conforming to the earlier draft rather
than to the final standard.

Even with the early version, though, it's not clear that putting an
auto_ptr in a standard container is a good idea.  Containers and
algorithms copy things, and an auto_ptr is fragile; if you look at it,
you destroy its value.  Proving that an algorithm is correct for a
sequence of auto_ptrs is much harder than proving that it's correct
for an ordinary type.

I don't think that anyone has done the work of checking which
containers and algorithms are auto_ptr-safe; you'll be taking your
chances.  auto_ptr violates some properties that are so fundamental
that a container or algorithm implementor is likely to rely on them
without even consciously thinking about them.


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/03/02
Raw View
Anthony Shipman wrote:
>
> sbnaran@bardeen.ceg.uiuc.edu (Siemel Naran) writes:
>
> >So disregard my earlier comments.  A copy constructor is required
> >for std::auto_ptr if we are to return such objects.
>
> Speaking about the copy constructor on auto_ptr...
>
> [ This is using SGI's STL implementation on IRIX 6.5]
>
> I use auto_ptr in attributes like:
>
>     protected:
>         std::vector<auto_ptr<T> >       data;
>
> Then when the destructor for data is called the vector's destructor will
> call the destructor of auto_ptr which in turn will do a delete on each
> of the objects pointed to by data.  This simplifies my destructors.
>
> Unfortunately I cannot do this with the std::map class.  If I write
>
>         std::map<K, auto_ptr<T> >       my_map;
>
> Then there is required to be a class
>
>         std::pair<const K, auto_ptr<T> >
>
> and std::pair requires a copy constructor of the form
>
>         auto_ptr<T> (const auto_ptr<T>& other)
>
> because of its constructor
>
>         std::pair(const T1& a, const T2& b) : first(a), second(b) {}
>
> and auto_ptr<T> only has the copy constructor
>
>         auto_ptr<T> (auto_ptr<T>& other)
>
> because copying requires modifying the object, by doing a .release().
>
> Would it be sensible to change auto_ptr to declare its ptr attribute to be
> mutable?  Then the copy constructor can pretend to be const as it moves
> the pointer around behind the curtain ie
>
>     template <class X> class auto_ptr {
>     private:
>       mutable X* ptr;
>
>     public:
>       typedef X element_type;
>       explicit auto_ptr(X* p = 0) __STL_NOTHROW : ptr(p) {}
>       auto_ptr(const auto_ptr& a) __STL_NOTHROW : ptr(a.release()) {}

The problem is that the auto_ptr is then even mutable if
explicitly declared as const:

void sink(auto_ptr<int>);

void foo()
{
  const auto_ptr<int> p(new int);
  // ...
  sink(p); p changed, despite being declared const!

  *p = 5; // Undefined behaviour (dereference of NULL)
}

With the current auto_ptr, the compiler will flag sink(p) as
illegal. This makes sense, because the expression _does_
change p, despite it being const.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: James.Kanze@dresdner-bank.com
Date: 1999/03/03
Raw View
In article <920339950.433916@aloomba.aaii.oz.au>,
  als@aaii.com.au (Anthony Shipman) wrote:
> sbnaran@bardeen.ceg.uiuc.edu (Siemel Naran) writes:
>
> >So disregard my earlier comments.  A copy constructor is required
> >for std::auto_ptr if we are to return such objects.
>
> Speaking about the copy constructor on auto_ptr...
>
> [ This is using SGI's STL implementation on IRIX 6.5]
>
> I use auto_ptr in attributes like:
>
>     protected:
>  std::vector<auto_ptr<T> > data;
>
> Then when the destructor for data is called the vector's destructor will
> call the destructor of auto_ptr which in turn will do a delete on each
> of the objects pointed to by data.  This simplifies my destructors.
>
> Unfortunately I cannot do this with the std::map class.

You can't legally do it with vector, either.  Part of the reason why the
standard auto_ptr is so complicated is precisely to allow returning it
from a function, but to disallow its use in the standard containers.
The semantics of the earlier "copy" constructor were not copy, and do
not support use in a standard container.  Any standard container.

The solution is to write your own reference counted pointer for such
cases.  You can find examples in either "More Effective C++", by Scott
Meyers, or in Barton and Nackman.

--
James Kanze                                           GABI Software, S   rl
Conseils en informatique orient    objet  --
                          --  Beratung in industrieller Datenverarbeitung
mailto: kanze@gabi-soft.fr          mailto: James.Kanze@dresdner-bank.com

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: als@aaii.com.au (Anthony Shipman)
Date: 1999/03/02
Raw View
sbnaran@bardeen.ceg.uiuc.edu (Siemel Naran) writes:

>So disregard my earlier comments.  A copy constructor is required
>for std::auto_ptr if we are to return such objects.

Speaking about the copy constructor on auto_ptr...

[ This is using SGI's STL implementation on IRIX 6.5]


I use auto_ptr in attributes like:

    protected:
 std::vector<auto_ptr<T> > data;

Then when the destructor for data is called the vector's destructor will
call the destructor of auto_ptr which in turn will do a delete on each
of the objects pointed to by data.  This simplifies my destructors.

Unfortunately I cannot do this with the std::map class.  If I write

 std::map<K, auto_ptr<T> > my_map;

Then there is required to be a class

 std::pair<const K, auto_ptr<T> >

and std::pair requires a copy constructor of the form

 auto_ptr<T> (const auto_ptr<T>& other)

because of its constructor

 std::pair(const T1& a, const T2& b) : first(a), second(b) {}

and auto_ptr<T> only has the copy constructor

 auto_ptr<T> (auto_ptr<T>& other)

because copying requires modifying the object, by doing a .release().


Would it be sensible to change auto_ptr to declare its ptr attribute to be
mutable?  Then the copy constructor can pretend to be const as it moves
the pointer around behind the curtain ie

    template <class X> class auto_ptr {
    private:
      mutable X* ptr;

    public:
      typedef X element_type;
      explicit auto_ptr(X* p = 0) __STL_NOTHROW : ptr(p) {}
      auto_ptr(const auto_ptr& a) __STL_NOTHROW : ptr(a.release()) {}

--
Anthony Shipman,                "You've got to be taught before it's too late,
AAII, Melbourne, Australia       Before you are six or seven or eight,
als@aaii.com.au                  To hate all the people your relatives hate,
+61 3 92477679                   You've got to be carefully taught."  R&H
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]