views:

359

answers:

8

I can't use shared_ptr in my project, no boost :(

So, I'm having a class roughly similar to the one below:

class MyClass
{
private:
  std::auto_ptr<MyOtherClass> obj;
};

Now, I want to store the instances of above class in std::vector. Is it safe? I've read here that it's wrong to use std::auto_ptr with STL containers. Does it apply to my situation here?

+2  A: 

I've posted a question as a follow-up to this answer, see http://stackoverflow.com/questions/704780/class-containing-autoptr-stored-in-vector.

Assming your class does not have a user-defined copy constructor, then no, it is probably (see below) not safe. When your class is copied (as will happen when it is added to a vector) the copy constructor of the auto_ptr will be used. This has the weird behaviour of tranferring ownership of the thing being copied to the copy and, so the thing being copied's pointer is now null.

It is possible, though unlikely, that you actually want this behaviour, in which case an auto_ptr is safe. Assuming you do not, you should either:

  • add a copy constructor to manage the copying

Note this is not enough - see the follow-up question mentioned above for more info.

or:

  • use a smarter, possibly reference counted pointer, such as one of the boost smart pointers
anon
In the example in the question, I feel it is worth pointing out that there is a copy constructor generated by the compiler.
1800 INFORMATION
modified answer to cover this
anon
You actually do NOT want the implicit behavior, as it violates a basic STL assumption on your type: All copies of an object are equal. E.g. std::sort WILL break your vector, if copying a MyClass to use as a pivot strips the source object in the vector from its MyOtherClass .
MSalters
yeah if you rely on that implicit behavior, you have undefined behavior. your type T has to be copy constructible. can't work with auto_ptr as a member.
Johannes Schaub - litb
@MSalters - maybe he doesn't want to sort the vector?
anon
@litb - who are you replying to? if me, I dispute that merely storing a class containing an auto_ptr in a vector gives undefined behaviour. Undesirable behaviour, yes.
anon
nope. this is quite clear. all containers require their element type to be Assignable and CopyConstructible. note that this is a concept - doesn't mean that merely a cctor is needed. It means that the copy and the original are equivalent. It also means you can copy from a const T
Johannes Schaub - litb
It means that a container may make a copy, and then still use the old version (imagine what happens if the copy constructor of an element throws - half of the elements were copied, and half not. and push_back has a strong exception safety guarantee!
Johannes Schaub - litb
Well, I disagree. There is no undefined behaviour here, and that is all that counts at the end of the day.
anon
It is undefined behavior. Container requirements (23.1/3 of the standard) says "The type of objects stored in these components must meet the requirements of CopyConstructible types (20.1.3), and the additional requirements of Assignable types."
Michael Burr
The standard also has a note in 20.4.5/3 (Class template auto_ptr) saying: "auto_ptr does not meet the CopyConstructible and Assignable requirements for Standard Library container elements and thus instantiating a Standard Library container with an auto_ptr results in undefined behavior."
Michael Burr
+1  A: 

Copying MyClass object will cause either call to assignment operator or copy constructor. If they are not overloaded to handle auto_ptr<> in unusual way, they will propagate the call to copy constructor (or assignment operator) to the auto_ptr<> member. This may lead to problems described in question you had linked.

+6  A: 

It is not safe, bacause when container will copy MyClass instnace default copy operator will call copy for all members - and for auto_ptr member too and we will have same situation as you describe in your question ( storing auto_ptr in container )

BTW: for avoid confusion at compile time add

private:
  MyClass& operator=( const MyClass& );  
  MyClass( const MyClass& );

compiler output error if you will try use copy operators, this can save you from hours of debug.

bb
anon
ANd that's exactly why it's a good idea in this case. It turns a possible runtime error (null pointer dereference) into a compile-time error (MyClass can't be copied)
MSalters
A: 

It will not work. auto_ptr doesn't count references which means at the first destructor call your pointer will be freed.

Use boost::shared_ptr instead.

Edouard A.
+1  A: 

The reason why it is not safe to instanciate a vector of auto_pointer is that there is an algorithm : sort(), that will do a copy of one object in your container on the stack. (sort() implements quicksort which needs a "pivot") And therefore deleting it when going out of scpope of the sort() function. As well any algorithm, or function of your own that are able to take your container as parameter, and copy one of its object on the stack will cause this issue as a result.

Well in your case, it is simple you must ensure your class does not behaves as an auto_ptr, or ensure you will never call such function/algorithm that can delete your underlying objects. The first solution is best, according to me :)

So your copy constructor and your affectation operator as well, should not give away property of the pointer object.

The best way to achieve that is to wrapp a boost smart pointer instead of an auto_ptr, to make your container safe when calling such function/algorithm.

By the way according to me, defining a better copy constructor/affectation operator to bypass this issue is not a good solution: I can't see a good copy constructor implementation (and affectation operator as well) that could keep safe the result of applying the sort() algorithm.

yves Baumes
+3  A: 

As Neil Butterworth said, auto_ptr is probably not the way to go.

boost::shared_ptr clearly is, but you say you can't use boost.

Let me mention that you could download boost, extract what you need for shared\ptr only using the bcp tool and use boost::shared_ptr. It would only mean a few added hpp files in your project. I believe it's the right way to go.

Benoît
Benoit, I'm on GCC 4.1.2, didn't know shared_ptr is available in tr1. Someone gave a hint; the comment is lost after StackOverflow's maintenance. I'm now using std::tr1::shared_ptr.
artknish
+3  A: 

It is not valid to have an object that contains an auto_ptr in a standard container. You run into undefined behavior. Two common problems:

  • std::vector<>::resize copies its argument into each created element. The first copy will "succeed" (see below why not), but each further copy will be empty, because the element copied is also empty!
  • If something during reallocation throws, you can happen to have some elements copied (to a new buffer) - but the copy being thrown away - and other elements not, because push_back must not have any effects if an exception is being thrown. Thus some of your elements are now empty.

As this is all about undefined behavior it does not really matter. But even if we try to come up with this behavior based on what we think is valid, we would fail anyway. All the member functions like push_back, resize and so on have a const reference that takes an object of type T. Thus, a reference of type T const& is tried to copied into elements of the vector. But the implicitly created copy constructor/copy assignment operator looks like T(T&) - that is, it requires a non-const object to be copied from! Good implementations of the Standard library check that, and fail to compile if necessary.

Until the next C++ version, you have to live with this. The next one will support element types that are merely movable. That is, a moved object does not need to be equal to the object moved to. That will allow putting streams, transfer-of-ownership pointers and threads into containers.

See what the Standard says for this (17.4.3.6):

In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ Standard Library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.

In particular, the effects are undefined in the following cases:

  • for types used as template arguments when instantiating a template component, if the operations on the type do not implement the semantics of the applicable Requirements subclause (20.1.5, 23.1, 24.1, 26.1).
Johannes Schaub - litb
Sorry - you still have not demonstrated UB. Please post some actual C++ CODE (not hand-waving) that does so. I have the utmost respect for your opinions, but I really do think that you are wrong here.
anon
the very nature of UB is that it can't be demonstrated :)
Johannes Schaub - litb
Not so! char * p = 0; *p = 'x'; // undefined behaviour
anon
I thought you already know how to do it. struct f { auto_ptr<int> p; }; vector<f> q; // undefined behavior
Johannes Schaub - litb
i'll leave this issue as it is now, because you already know everything to know about it ;) see you again on usenet
Johannes Schaub - litb
Now you have completely lost me (and any onlookers, I suspect). I'm going to post a separate question about this tomorrow
anon
it smells blood around here ... :P, but it is really interesting, I wasn't aware of the next container evolution. I really need to get this copy of standard draft too ...
yves Baumes
yves, haha :P all blood fixed already. yeah next standard is awesome
Johannes Schaub - litb
+1  A: 

If you want to use a class that uses auto_ptr in a container, you can just provide a copy-constructor and assignment operator yourself:

class MyClass
{
private:
  const std::auto_ptr<MyOtherClass> obj; // Note const here to keep the pointer from being modified.

public:
  MyClass(const MyClass &other) : obj(new MyOtherClass(*other.obj)) {}
  MyClass &operator=(const MyClass &other)
  {
      *obj = *other.obj;
      return *this;
  }
};

But as mentioned elsewhere, the standard lets containers make copies and assignments and assumes that the contained classes will behave in a specific manner that auto_ptr violates. By defining the methods above, you can make a class that contains an auto_ptr behave. Even if your implementation works fine with auto_ptrs, you run the risk of finding another implementation doesn't work. The standard only make guarantees of performance and observable behaviour, not implementation.

Eclipse