Redeeming qualities of auto_ptr?

Started by
30 comments, last by Emmanuel Deloget 17 years, 1 month ago
Quote:Original post by Emmanuel Deloget
Regarding auto_ptr<>, I don't look iit as an evil class. It has its use - if you don't need the can-share semantics of shared_ptr, auto_ptr workds just fine.


Or if you need to put it in a container.

Why is there no standard smart pointer (or did I overlook something?) with "copy the pointed-at thing" semantics? I find myself hand-rolling it all the time. (Of course, if the copy is expensive or impossible, then shared_ptrs become necessary, yes.)
Advertisement
Quote:Original post by Zahlman
Quote:Original post by Emmanuel Deloget
Regarding auto_ptr<>, I don't look it as an evil class. It has its use - if you don't need the can-share semantics of shared_ptr, auto_ptr works just fine.


Or if you need to put it in a container.

Yep. This one was obvious :) the TransferOwnership semantic prohibits the use of the auto_ptr in any kind of container.

Quote:Original post by Zahlman
Why is there no standard smart pointer (or did I overlook something?) with "copy the pointed-at thing" semantics? I find myself hand-rolling it all the time. (Of course, if the copy is expensive or impossible, then shared_ptrs become necessary, yes.)


You'll have to make sure that the object you want to copy is CopyConstructible - if it's not, then you'll have to fallback to another solution, which means that your smart pointer will behave differently depending on the object it encapsulates - that would be quite difficult to use (even if the implementation part is not that hard - if you use type traits). This is, IMHO, a no-no for a standard library, whose effects has to be known and consistent.

Regards,
Quote:Original post by Emmanuel Deloget
Quote:Original post by Zahlman
Why is there no standard smart pointer (or did I overlook something?) with "copy the pointed-at thing" semantics? I find myself hand-rolling it all the time. (Of course, if the copy is expensive or impossible, then shared_ptrs become necessary, yes.)


You'll have to make sure that the object you want to copy is CopyConstructible - if it's not, then you'll have to fallback to another solution, which means that your smart pointer will behave differently depending on the object it encapsulates - that would be quite difficult to use (even if the implementation part is not that hard - if you use type traits). This is, IMHO, a no-no for a standard library, whose effects has to be known and consistent.

Regards,


I dont see how this is a problem, if you have a shared pointer that copies the pointed at thing then the pointed at thing should obviously be copy constructable if its not it seems logical for the standard to require a diagnostic or if you prefer it could fall back on undefined behaviour.

IMO its the same as with the standard containers, if your class isnt copy constructable then (to my knowledge) they dont provide some alternative implementation with different semantics so why would a copying shared pointer provide an alternative?
Quote:
Why is there no standard smart pointer (or did I overlook something?) with "copy the pointed-at thing" semantics? I find myself hand-rolling it all the time. (Of course, if the copy is expensive or impossible, then shared_ptrs become necessary, yes.)


Perhaps I'm misunderstanding the functionality you want, but why not just use a non-pointer if you need operator= to "copy the thing?"
Quote:
As we all like to think, auto_ptr == evil because of the ownership-transfer for every assign


std::vector< std::auto_ptr<int> > aVector;aVector.push_back( std::auto_ptr<int>(new int(5)) );

error C2558: class 'std::auto_ptr<_Ty>' : no copy constructor available or copy constructor is declared 'explicit'

My definition of 'evil' code constructs doesn't include classes that issue nice, easy to understand errors when they are misused. If you abandon const-correctness, then you're on your own.

IMO, writing clear code for the benefit of other developers is one of the most important skills for any coder working in a team. Each one of the smart pointers says something very specific to someone reading the code(as do references, but raw pointers are very vague). The meaning of a function returning a shared_ptr is very different from one returning an auto_ptr.
Quote:Original post by jpetrie
Quote:
Why is there no standard smart pointer (or did I overlook something?) with "copy the pointed-at thing" semantics? I find myself hand-rolling it all the time. (Of course, if the copy is expensive or impossible, then shared_ptrs become necessary, yes.)


Perhaps I'm misunderstanding the functionality you want, but why not just use a non-pointer if you need operator= to "copy the thing?"


You cant store a container of base classes by value and get polymorphic behaviour.
Fair point!

But "copy the thing that is pointed to" pointers alone won't be enough for that to work in general, you'd need a standard polymorphic clone of some sort, or a special container, or a number of other extra enhancements.
Quote:Original post by jpetrie
Quote:
Why is there no standard smart pointer (or did I overlook something?) with "copy the pointed-at thing" semantics? I find myself hand-rolling it all the time. (Of course, if the copy is expensive or impossible, then shared_ptrs become necessary, yes.)


Perhaps I'm misunderstanding the functionality you want, but why not just use a non-pointer if you need operator= to "copy the thing?"


Polymorphism, Pimpls, virtual clone idiom etc. (If you can standardize the name of the clone() function, it should be possible to use SFINAE introspection to make use of it?)

Quote:Original post by Emmanuel Deloget
Quote:Why is there no standard smart pointer (or did I overlook something?) with "copy the pointed-at thing" semantics? I find myself hand-rolling it all the time. (Of course, if the copy is expensive or impossible, then shared_ptrs become necessary, yes.)


You'll have to make sure that the object you want to copy is CopyConstructible


Which I addressed :)

Quote:if it's not, then you'll have to fallback to another solution, which means that your smart pointer will behave differently depending on the object it encapsulates - that would be quite difficult to use (even if the implementation part is not that hard - if you use type traits).


I guess it's the classic DWIM argument. Yeah, it would be more C++ to *offer* the copy-semantics handle, and make sure it doesn't compile with a noncopyable object (should be trivial), so that people will (manually, being alerted of what's going on) fall back to boost::shared_ptr in those instances.
Quote:Fair point!

But "copy the thing that is pointed to" pointers alone won't be enough for that to work in general, you'd need a standard polymorphic clone of some sort, or a special container, or a number of other extra enhancements.


Im not sure what you mean by a special container but it should be fairly easy, for example, consider the following (naive) minimal implementation.

// this version provides fast acess to the stored ptr at the cost of slightly// more memory usage, it is however possible to reduce memory usage by// adding another layer of indirection when accessing the stored ptr// used to maintain type information for copyingtemplate<typename BaseType>struct copy_ptr_storage{    virtual BaseType* copy(BaseType*) = 0;    virtual copy_ptr_storage<BaseType>* clone() = 0;};// stores the actual type infotemplate<typename BaseType, typename T>struct copy_ptr_impl : copy_ptr_storage{    BaseType* copy(BaseType* p)    {        return new new T(*static_cast<T*>(p));    }    copy_ptr_storage<BaseType>* clone()    {        return new copy_ptr_impl<BaseType, T>();    }};// the ptrstruct copy_ptr<typename BaseType>{    template<typename T>    copy_ptr(T* ptr):    type_ptr(new copy_ptr_impl<T>()),    base_ptr(ptr)    { }    copy_ptr(const copy_ptr<BaseType>& rhs):    type_ptr(rhs.type_ptr->clone()),    base_ptr(rhs.type_ptr->copy(rhs.base_ptr));    {    }    copy_ptr<BaseType&> operator=(const copy_ptr<BaseType>& rhs)    {        delete base_ptr;        base_ptr = rhs.type_ptr->copy(rhs.base_ptr);        delete type_ptr;        type_ptr = rhs.type_ptr->clone();    }    BasePtr& operator*()    {        return *base_ptr;    }    BaseType* oprator->()    {        return base_ptr;    }    ~copy_ptr()    {         delete type_ptr;        delete base_ptr;    }private:    copy_ptr_storage* type_ptr;    BaseType* base_ptr;};


With a little bit of work you can also get rid of the extra dynamic allocations for type_ptr.

On second thought that probally counts as the "stand alone polymorphic clone" that you mentioned but atleast the client doesnt need to know about it, as far as theyre concerned it "just works"

Edit:

Quote:Original post by Zahlman
Quote:Original post by jpetrie
Perhaps I'm misunderstanding the functionality you want, but why not just use a non-pointer if you need operator= to "copy the thing?"


Polymorphism, Pimpls, virtual clone idiom etc. (If you can standardize the name of the clone() function, it should be possible to use SFINAE introspection to make use of it?)


As i just demonstrated you can use the already standardized version, a copy constructor, using a clone detected with sfinae places more requirments on the user and (with some changes to my example, i.e. get rid of memory overhead and dynamic allocations) wouldnt be anymore effecient or usefull as both would require a single virtual function call...
Quote:Original post by Sneftel
Quote:Original post by Replicon
I'd call it 'dangerous' before 'strange' though, and depending on where the issue is encountered, it can yield behaviour that is very... misdirecting?

I wouldn't call it dangerous. Dangerous is something that works 98% of the time, or 100% on your particular computer and 50% on other people's, etc. Something that never works is easy to detect, and something that's easy to detect is easy to fix.


It's no weapon of mass conternation, but it is still dangerous to one's time in the hands of the underinformed or the mentally disturbed. It's unlocked, implicit behavior, also sets dangerous precident in terms of interface design to the impressionable.

I look forward to the implementation of std::unique_ptr as a sane, complete replacement of std::auto_ptr, which I've had reason to use in some scenarios.

This topic is closed to new replies.

Advertisement