Why vector.push_back(auto_ptr) wouldn't compile?
Asked Answered
L

4

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?

Lassalle answered 9/9, 2011 at 8:56 Comment(4)
I know I should not use the auto_ptr in stl because of the copy semantic. But my question is how the stl is implemented so it can forbid you doing so? In my sample code, it cannot even compile.Lassalle
Can you post the compile error?Leeds
@xanatos: No copy constructors are const!Iveyivie
@xanatos: No references are const! They are already inherently immutable!Iveyivie
I
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.

Iveyivie answered 9/9, 2011 at 9:10 Comment(14)
I strongly disagree, std::auto_ptr is perfect to solve a set of problems. Sadly the developers misuse it.Unpeople
@Alessandro: If it's so "perfect", why is it deprecated already?Iveyivie
@Alessandro: I strongly disagree, std::unique_ptr is perfect to solve that set of problems. Luckily, it's much harder to misuse it. :)Leeds
I tend to use std::auto_ptr<> to be explicit about passing ownership of objects. For that, it's brilliant, I think.Gastineau
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.Haunch
@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.Iveyivie
@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.Haunch
@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.Haunch
@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!Iveyivie
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.Unpeople
@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!Iveyivie
@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 vectorsHaunch
@David: I'm still not sure, but since push_back definitely causes an issue, I'll adjust my example. Thanks for the tip.Iveyivie
sorry, I didn't understand that it was a rhetoric question ;-)Unpeople
T
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)
}
Theall answered 9/9, 2011 at 9:20 Comment(0)
U
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.

Unpeople answered 9/9, 2011 at 8:59 Comment(4)
Or a decent single ownership pointer with move semantics, like std::unique_ptr.Leeds
have a look at items 13-17 of Effective C++, it is not specific with auto_ptr, but it is very useful to understand your problem.Unpeople
I know I should not use the auto_ptr in stl because of the copy semantic. But my question is how the stl is implemented so it can forbid you doing so? In my sample code, it cannot even compile.Lassalle
It's not just about push_back. A container needs to copy elements around internally, too.Iveyivie
C
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

Clastic answered 9/9, 2011 at 9:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.