Sign in to follow this  
Replicon

Redeeming qualities of auto_ptr?

Recommended Posts

As we all like to think, auto_ptr == evil because of the ownership-transfer for every assign:
auto_ptr<Foo> p1(new Foo);
auto_ptr<Foo> p2(p1); // Oh snap, p1 is now NULL!!
This is so reeking with evilness that the standards committee has made it part of the c++ standard that you should not be allowed to make STL containers of auto_ptrs (since many algorithms will cause elements in containers to be copied, and stuff will break as a result). The question: Does auto_ptr have ANY redeeming properties, that would excuse it and make it worthy of being in the standard still? Once std::tr1 goes out, and all the "boost::*_ptr" stuff ends up being part of it, will there be ANY reason at all to ever use auto_ptr? Will it stay in the standard for any reason other than backwards compatibility?

Share this post


Link to post
Share on other sites
Its useful for functions that create an object and return a pointer to it, like factories (but I usually have these return shared_ptrs anyway). It can be used for simple scoped objects created with new() that you don't want to stack allocate for some reason, say if the object was very large ( I think boost also has a scoped_ptr, but I've never realy looked at it ).

But that said I tend to default to boost::shared_ptr for pretty much everything. I did a quick search in my current project and found a single auto_ptr, and that is a temporary thing I'm using while I test something.

Share this post


Link to post
Share on other sites
Quote:
Original post by Replicon
As we all like to think, auto_ptr == evil because of the ownership-transfer for every assign:


auto_ptr<Foo> p1(new Foo);
auto_ptr<Foo> p2(p1); // Oh snap, p1 is now NULL!!


This is so reeking with evilness that the standards committee has made it part of the c++ standard that you should not be allowed to make STL containers of auto_ptrs (since many algorithms will cause elements in containers to be copied, and stuff will break as a result).

The fact that auto_ptrs don't work with STL containers is a consequence of the lack of a const copy constructor combined with STL semantics, not an explicit design decision in its own right. If you wrote a smart pointer that worked like auto_ptr, and wrote a container that worked like std::vector, the two would automatically not work together.

And calling it "evil" is a bit of an overreaction. It's strange, yes. But since the auto_ptr copy constructor takes a non-const reference, there's no chance of it stomping all over structures you didn't expect it to stomp all over in properly const-correct code (your code IS const-correct, right?), and the worst case is simply a pointer you didn't expect to be NULL. Hardly the most oblique or difficult-to-troubleshoot part of C++.

Share this post


Link to post
Share on other sites
Quote:
Original post by SneftelAnd calling it "evil" is a bit of an overreaction. It's strange, yes.


That's very true, and I admit to wanting to make it look more dramatic than it really is. I'd call it 'dangerous' before 'strange' though, and depending on where the issue is encountered, it can yield behaviour that is very... misdirecting? But is explicitely defining who has ownership really that important? I've always felt it was like an unnecessary chore where the possible pitfalls far outweigh the benefits. Is there, for instance, a huge performance penalty incured from using a reference counted pointer (e.g. shared_ptr) in places where a basic auto_ptr type thing would have worked fine?

Share this post


Link to post
Share on other sites
Quote:

The question: Does auto_ptr have ANY redeeming properties, that would excuse it and make it worthy of being in the standard still? Once std::tr1 goes out, and all the "boost::*_ptr" stuff ends up being part of it, will there be ANY reason at all to ever use auto_ptr? Will it stay in the standard for any reason other than backwards compatibility?

Yes. It implements transfer of sole-ownership semantics, which none of the boost ptrs offer.

There's nothing dangerous about auto_ptr unless you fundamentally misunderstand
it's purpose - as smart pointer that can transfer ownership.

I could equally argue that boost::weak_ptr is dangerous because it can change from a valid pointer to a null pointer - or I could understand the semantics of the class I'm using.


The unique_ptr example linked is very silly indeed. It 'solves' the 'problem' with auto_ptr by removing the transfer of ownership semantics. If that replaces auto_ptr in the standard, then anyone wishing to return a dynamically allocated resource from a function will need to to use their own implementation of auto_ptr (or use a shared_ptr - which introduces its own 'dangers').

Quote:

From the linked paper:
You can safely put unique_ptr into containers. If the containers move elements around internally instead of copy them around (as proposed for the standard containers), unique_ptr will work without problems. If the container does use copy semantics for its value_type, then the client will be notified immediately (at compile time). There is no chance of a run time error due to a move being mistaken for a copy.


Oh dear. Someone should tell the author that std::auto_ptrs currently cause std container to fail at compile time, and that whatever move semantics end up in C++0x will allow containers to be implemented in a way compatible with auto_ptr.

unique_ptr does add useful functionality - the custom delete (a la shared_ptr), but it really needs to retain its transfer of ownership semantics.

Share this post


Link to post
Share on other sites
I find auto_ptr extremly useful in class constructors for pointing out that the class now owns that pointer instead of making an internally owned copy, for example if i had a tecture class that took a pointer to the data in a certain format i would have to constructors one which took an ordinary pointer to the data and made an internal copy and one which took an auto_ptr and canabalized the given data and memory for its own use.

Share this post


Link to post
Share on other sites
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.

As for misdirecting.... maybe. I've never been surprised by auto_ptr, but that's because I know and can expect how it works. Alongside scoped_ptr, I only end up using it in special situations where I want exactly that behavior.

Quote:
But is explicitely defining who has ownership really that important?

Well, no. Not anymore. I use shared/weak ptr for virtually everything now (including Ogre objects; ask me how!) and that makes lifetime management very easy. In really tight loops you should definitely avoid weak_ptr and probably shared_ptr, but in those circumstances you're peering at the code closely enough that there's little ease-of-use to be gained from auto_ptr over raw pointers. The big remaining advantage is exception safety, but you won't need that in a tight loop.

I will say, though, that when working with a team which is unwilling for historical reasons to embrace smart pointers, auto_ptr can go a long way towards maintaining sanity.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sharlin
Fortunately, C++0x will allow us to replace auto_ptr with the goodness that is unique_ptr.

Not to mention that tr1::shared_ptr<> will be imported back into the std namespace - so we'll have std::shared_ptr, std::weak_ptr and std::enable_shared_from_this.

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. I use it quite extensively for objects that need to be allocated. I find it weird - because of its semantics - but once you get those right, it's still a useful tool.

Regards,

Share this post


Link to post
Share on other sites
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.)

Share this post


Link to post
Share on other sites
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,

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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?"

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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 copying
template<typename BaseType>
struct copy_ptr_storage
{
virtual BaseType* copy(BaseType*) = 0;
virtual copy_ptr_storage<BaseType>* clone() = 0;
};

// stores the actual type info
template<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 ptr
struct 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...

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey
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.


Quote:
Goooooooooooogle
Your search - std::unique_ptr - did not match any documents.


:( References (pun intended) please?

@Julian: Interesting. I'll play around with it and see if it works, and if it's possible to get rid of the extra pointer. :)

Share this post


Link to post
Share on other sites
Oh, wait, I just remember: if you need to perform a deep copy of an object, why do you store it in a smart pointer anyway (as opposed to, say, keep an instance of the object)? That sounds weird. If your concern is about stack memory vs. heap memory, one can easily create objects that stores data on the heap, while the object themselves are on the stack (the way std::vector works).

So: what would be a typical use of such feature? (let's call it copyable_ptr<>)

Share this post


Link to post
Share on other sites
Quote:
Original post by Emmanuel Deloget
Oh, wait, I just remember: if you need to perform a deep copy of an object, why do you store it in a smart pointer anyway (as opposed to, say, keep an instance of the object)?


This was already asked:

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?)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this