Jump to content
  • Advertisement
Sign in to follow this  
yacwroy

C++: Why is std::vector(std::auto_ptr(MYTYPE)) bad?

This topic is 3717 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have just been converting much of my code to RAII. However, in a few places I have vectors of pointers that I would like to convert to auto_ptr. std::vector<std::auto_ptr<MYTYPE> > is exactly what I would want, except that I hear it doesn't work because std::auto_ptr doesn't fit the requirements for std::vector. AFAIK, the one requirement it doesn't meet is that it doesn't have a standard copy constructor (which requires that the copied item is const). To me it looks like it _should_ fit the requirements as long as I promise never to copy it, which I don't plan on doing. I understand that during inserts, erases and sometimes during resizes that the location of members in RAM change, which requires copy-constructing - but, shouldn't std::vector should be happy to use a non-const copy-constructor here since it's really a move or copy-construct + delete, hence the member being copied is already being treated as mutable. This would make std::vector<std::auto_ptr<MYTYPE> > correct for use in most applications, but still make copying it illegal. Secondly, if I can't use std::vector<std::auto_ptr>, is there a simple alternative? Thanks for all replies.

Share this post


Link to post
Share on other sites
Advertisement
You may not copy it yourself, but it may get copied quite alot inside std::vector, or any other std:: container for that matter. You can use boost::shared_pointer.

Share this post


Link to post
Share on other sites
Quote:
Original post by yacwroy
I understand that during inserts, erases and sometimes during resizes that the location of members in RAM change, which requires copy-constructing - but, shouldn't std::vector should be happy to use a non-const copy-constructor here since it's really a move or copy-construct + delete, hence the member being copied is already being treated as mutable.


A non-const copy-constructor isn't a copy-constructor. It's just a constructor (or perhaps a movement one). For reasons related to sloppy standards and annoying language semantics, std::vector implementations are allowed to rely on the invariant that a copy of the object can be made—which implies that the original and the copy are equally valid. In the case of std::auto_ptr, if the vector created a 'copy', then decided to dump the 'copy' and keep the original for whatever reason, the pointer would be destroyed and you'd lose data.

Share this post


Link to post
Share on other sites
To expand on the copying by std::vector: initially the vector will internally contain an array of objects with a certain length N. The moment you call push_back for the (N+1)th time the vector will allocate a new larger array, and copy everything from the first array to the new one. Since you don't know N, you cannot now when the copy constructors will get called. You could get around this by using the reserve() method of std::vector. But the suggestion to use shared_ptr is by far superior :)

Share this post


Link to post
Share on other sites
Quote:
Original post by yacwroy
To me it looks like it _should_ fit the requirements as long as I promise never to copy it, which I don't plan on doing.

Ignore the previous posts: vector<auto_ptr> will do exactly the right thing on insertion, the copying on expansion will do the right thing, and all that. There's not problem there.

The problem is that operations on the vector may make temporary copies of elements of the vector (for example, std::sort, which takes a copy of an element as a pivot value then discards it). Next thing you know, you've lost data. To this end, the ISO C++ committee decided to break auto_ptr by making its copy constructor take a non-const reference, where standard containers have a const reference. The idea is that it will not compile, just in case you might want to perform an operation that might be unsafe.

The right thing to do is to use a refcounted pointer, not auto_ptr, with a standard library container.

Share this post


Link to post
Share on other sites
Quote:
Original post by Bregma
Ignore the previous posts: vector<auto_ptr> will do exactly the right thing on insertion, the copying on expansion will do the right thing, and all that.

Ah yes, you are right. I've never even considered this. Maybe I should start thinking a bit more ;-)

Share this post


Link to post
Share on other sites
Thanks.

shared_ptr is a good suggestion. Unfortunately, I'm not really a fan of shared_ptr though, and it's way too heavy for this use (not that that's too important).

I guess I better stay compliant and not just hope that a vector<auto_ptr> works. Thanks for confirming that it's illegal.

I've come up with a sort-of alternative, write my own template which encapsulates std::vector<T*> and calls delete on all pointers upon destruction.


template <typename T>
class my::auto_vector {
public:
auto_vector() {}
~auto_vector() {
for(std::vector<T*>::iterator it = m_items.begin(); it = m_items.end(); it++) {
delete (*it);
}
}
std::vector<T*> m_items;
};



I guess I could also specify the std::vector functions that would be appropriate within auto_vector, and make m_items private. But it'll do for now.

I saw someone suggesting adding auto_vector to ::std
http://www.relisoft.com/resource/auto_vector.html
Not sure what is happening with this though.

Actually, I think I'll use that implementation eventually.

Share this post


Link to post
Share on other sites
One more thing I just thought of:

Even though when objects are to be moved by std::vector (because of insert / erase or resizing) they are mutable, they will still need to be copied with a const copy-constructor so that if the copy-constructor throws partway through the operation, the vector's original contents are still ok, and RAII is done / leaks & corruption are prevented.

Of course, this would require that erase / insert operations relocate the entire vector, which is still linear but worst-case (and worse than the specs at www.cplusplus.com state - don't know where the official ones are).

Share this post


Link to post
Share on other sites
Quote:
Original post by yacwroyUnfortunately, I'm not really a fan of shared_ptr though, and it's way too heavy for this use (not that that's too important).

I'm sorry, that doesn't make sense.
What's heavy about shared_ptr?
And you don't need to be a "fan", you just have to realize that it's a good solution to the problem.

Moreover, instead of reinventing the wheel, you could just use boost's pointer container, as already suggested. it does pretty much what you were planning to do, but is much more likely to be 1) correct, 2) efficient.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!