6

I learned that STL can forbid programmer putting an auto_ptr into a container. For example following code wouldn't compile:

    auto_ptr<int> a(new int(10));
    vector<auto_ptr<int> > v;
    v.push_back(a);

auto_ptr has the copy constructor, why this code can even compile?

frinker
  • 63
  • 6

4 Answers4

11

Looking at the definition of std::auto_ptr:

namespace std {

    template <class Y> struct auto_ptr_ref {};


    template <class X>
    class auto_ptr {
    public:
        typedef X element_type;

        // 20.4.5.1 construct/copy/destroy:
        explicit           auto_ptr(X* p =0) throw();
                           auto_ptr(auto_ptr&) throw();
        template <class Y> auto_ptr(auto_ptr<Y>&) throw();

        auto_ptr&                      operator=(auto_ptr&) throw();
        template <class Y> auto_ptr&   operator=(auto_ptr<Y>&) throw();
        auto_ptr&                      operator=(auto_ptr_ref<X>) throw();

        ~auto_ptr() throw();

        // 20.4.5.2 members:
        X&     operator*() const throw();
        X*     operator->() const throw();
        X*     get() const throw();
        X*     release() throw();
        void   reset(X* p =0) throw();

        // 20.4.5.3 conversions:
                                    auto_ptr(auto_ptr_ref<X>) throw();
        template <class Y> operator auto_ptr_ref<Y>() throw();
        template <class Y> operator auto_ptr<Y>() throw();
    };

}

Although there is a copy-constructor, it takes a reference to non-const. Temporaries may not bind to this, so the type is effectively prohibited from working inside containers in any place where temporaries are used; in addition, push_back accepts a reference to const, so due to const-correctness it's impossible for the new, internal element to by copy-constructed from push_back's argument.

(That Wikipedia page says that "because of its copy semantics, auto_ptr may not be used in STL containers that may perform element copies in their operations"; this doesn't mean that containers magically examine the code inside the copy constructor to decide whether it wants to make the type work as an element type. Instead, it's just about the function signature.)

Anyway, std::auto_ptr is deprecated as of C++11 because, in the opinion of some, std::auto_ptr is silly. Sorry, std::auto_ptr.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    I strongly disagree, std::auto_ptr is perfect to solve a set of problems. Sadly the developers misuse it. – Alessandro Teruzzi Sep 09 '11 at 09:16
  • @Alessandro: If it's so "perfect", why is it deprecated already? – Lightness Races in Orbit Sep 09 '11 at 09:19
  • 4
    @Alessandro: I strongly disagree, std::unique_ptr is perfect to solve that set of problems. Luckily, it's much harder to misuse it. :) – R. Martinho Fernandes Sep 09 '11 at 09:21
  • I tend to use std::auto_ptr<> to be explicit about passing ownership of objects. For that, it's brilliant, I think. – Jörgen Sigvardsson Sep 09 '11 at 09:23
  • I don't think *temporaries* have anything to do with the problem. For starters, in the question, the call to `push_back` fails and there is no temporary involved: the argument to `push_back` is a const reference, and is used as the argument to the constructor of an `auto_ptr` internally, binding the *const lvalue* (not temporary) to the argument of the copy constructor fails, with no temporaries involved at any point. – David Rodríguez - dribeas Sep 09 '11 at 09:24
  • @David: There's more to the lifetime of a container than `push_back` (though admittedly, since `push_back` takes a ref-to-`const` then that is a problem _too_). Whether it's that or temporaries -- and it's most likely both -- is besides the point: the heart of the matter is the ref-to-non-`const`. – Lightness Races in Orbit Sep 09 '11 at 09:26
  • @R. Martinho Fernandes: `std::unique_ptr` is a better solution, but prior to C++0x people abused other smart pointers for no particular reason. Think on how many `shared_ptr` have you seen where ownership is *unique* and not *shared*... I myself have seen quite a bit, that or factories that return `shared_ptr` forcing that choice of smart pointer to the users... Saying that `std::auto_ptr` is *silly* is absurd, it serves/served a purpose and what is *silly* is trying to use it for things for which it was never designed. Damn, I feel like downvoting just for that comment... I won't though. – David Rodríguez - dribeas Sep 09 '11 at 09:27
  • @Tomalak Geret'kal: Right there is more to the lifetime of the objects in the container than `push_back` in general, but if you cannot insert the object in the container, there is very little of lifetime *in* the container at all. The lifetime could be an issue if you were at least able to add the values to the container, and it then failed for any other reason. Then again, I cannot think on any operation of `std::vector` where temporaries of the `T` type would be created --I have not thought it over really, there might be cases. – David Rodríguez - dribeas Sep 09 '11 at 09:30
  • @David: Don't be ridiculous! Not only was it obviously in jest, but auto_ptr was deprecated for a reason. Let's stop focusing on the little things and consider the wider point! – Lightness Races in Orbit Sep 09 '11 at 09:30
  • 1
    Because the syntax is unusual and generate confusion (as many other thing in c++). It will be a long discussion, in my personal opinion the biggest problem with auto_ptr was the fact that it was the only smart_ptr in the standard library and the developers try to use it in an improper way. – Alessandro Teruzzi Sep 09 '11 at 09:31
  • @Alessandro: I agree, I think. Ultimately it's highly subjective whether `auto_ptr` is good or bad, and any debate on the matter will be unconstructive. I've made my point and stated my opinion in my answer; feel free to write your own contrasting answers, but I won't be debating it here! – Lightness Races in Orbit Sep 09 '11 at 09:33
  • @Tomalak: I have not downvoted, nor have the intention of doing so even if I cannot appreciate the joke :) I have not upvoted either, for the reasons that I have tried explaining: I don't think there is any *temporary* involved in this particular case, and I cannot think of a reason why `std::vector` could create temporaries internally in any of the operations. Note that `resize(x)` will implicitly create a temporary if you do not provide a second argument, but that is *outside* of the container, and that would just limit an operation, which is limited for other types that do work in vectors – David Rodríguez - dribeas Sep 09 '11 at 09:36
  • @David: I'm still not sure, but since `push_back` *definitely* causes an issue, I'll adjust my example. Thanks for the tip. – Lightness Races in Orbit Sep 09 '11 at 09:37
  • sorry, I didn't understand that it was a rhetoric question ;-) – Alessandro Teruzzi Sep 09 '11 at 09:38
6

On the particular issue of how does the compiler detect that situation (or how does the STL cause an error there), you should read the exact output of the compiler, it will contain a bunch of errors that will lead to failure to perform a conversion from const X to X as it discards the const qualifier, where X can either be std::auto_ptr<> directly or else an internal detail type.

In particular, std::vector::push_back takes the argument by const &, and internally it will try to copy construct an element inside the dynamic array using the available copy constructor, which in the case of std::auto_ptr requires a non-const reference. Something in the lines of:

void push_back( std::auto_ptr<int> const & x ) {
    // ensure enough capacity if needed...
    new (buffer + size()) std::auto_ptr<int>( x ); // !!! cannot bind x to non-const&
    // complete the operation (adjust end pointer, and such)
}
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
0

Because std::auto_ptr is not compatible with stl container.

std::auto_ptr is using single ownership copy semantic, the stl container needs to copy construct an object (and some algorithms need to assign it)

You should use a reference counted smart pointer (boost::shared_ptr)

EDIT

For example, this is the signature of push_back

void push_back ( const T& x );

The problem is that std::auto_ptr is special and the copy constructor and assign operator signature are different. They are NOT const. You modify an auto_ptr if you copy it.

auto_ptr& operator= (auto_ptr& a) throw();

auto_ptr (auto_ptr& a) throw();

You cannot provide an auto_ptr that fulfil the requirement of push_back.

Alessandro Teruzzi
  • 3,918
  • 1
  • 27
  • 41
0

The other answers are bang on about auto_ptr.

To do what you are trying to do use std::unique_ptr if its available to you (C++11) if not you can use a shared_ptr

jk.
  • 13,817
  • 5
  • 37
  • 50