6

I need to obtain auto_ptr from shared_ptr in my code. I can do reverse operation - convert auto_ptr to shared_ptr as shared_ptr has such constructor:

template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);

Can I convert shared_ptr to auto_ptr? Or it is impossible by design?

ks1322
  • 33,961
  • 14
  • 109
  • 164

5 Answers5

9

It is impossible by design as the object may be shared with other shared pointer and thus "fetching" it to auto_ptr may lead to deleting referenced object.

For same reason shared_ptr has no "release" member function as auto_ptr.

Edit:

Even if shared_ptr had some kind of "release" method or allowed to remove its reference without destroying the object it would not work for following case (threads A, B):

A: { 
A:     int count = sp.use_count();
  Context Switch
B: shared_ptr<bar> my_sp = weak_sp.lock();
B: // now use_count = 2 but A thinks it is 1
  Context Switch
A:     auto_ptr<bar> ap;
A:     if(count == 1) 
A:      ap.reset(sp.release()); 
A:      // actutally there is no sp.release but what if
A:      ap->foo();
A: }  // delete the object pointer by ap as it goes out of scope
  Context Switch
B: my_sp->foo(); // Ooops - object is deleted!
Artyom
  • 31,019
  • 21
  • 127
  • 215
  • shared_ptr's "unique" method could in principle tell you whether the shared_ptr has the only remaining reference to the object, in which case transfer to an auto_ptr would seem to be a bit more of a reasonable thing to do. But without any supporting API (ie a release method) there's no way. (And in my experience, shared_ptr code which makes use of unique or count is usually doing something wrong). – timday Jan 23 '11 at 12:00
  • 2
    @timday The problem with unique that it can cause race condition when weak_ptr is used. For example if current count is 1 but in other thread some user of weak pointer creates shared_ptr it may become 2 and your previous check would be invalid. – Artyom Jan 23 '11 at 12:15
  • @Alf P. Steinbach Yes it is correct because of danger of conversion of weak_ptr to shared_ptr after checking the "uniquness" and before the actual release. – Artyom Jan 23 '11 at 13:11
  • @Artyom: you say it's impossible, I've showed it is possible in a certain case. "Impossible" != "possible". Your answer is just plain wrong, sorry. – Cheers and hth. - Alf Jan 23 '11 at 13:14
  • @Alf: There are different `shared_ptr` implementations. The answer is vague, at worse, not wrong. It is indeed impossible in C++0x, for example. – GManNickG Jan 23 '11 at 13:20
  • @Alf P. Steinbach you can even cast the pointer to other structure and reset is use count to 0 or do other 101 things. This doesn't changes the fact that it is not what shared_ptr is designed for and if you want to cast shared_ptr to auto_ptr then maybe you are doing something wrong. Also your "possible" does not work in general case - so it is impossible as in general case there is no solution. – Artyom Jan 23 '11 at 13:21
  • @Artyom: Yes, it's stuff like this which leads me to think "bad code smell!" if I see unique()/count() used. IMHO they're there to support debugging and that's all. I guess a shared_ptr providing some sort of atomic "release_if_unique" method could add appropriate locking internally... but that's a whole new thing. – timday Jan 23 '11 at 13:30
  • @Gman: No, it's not different in C++0x. C++0x `std::shared_ptr` has both `use_count` and `get_deleter`. Works fine. :-) – Cheers and hth. - Alf Jan 23 '11 at 13:36
  • @Artyom: your answer is incorrect. i'm not going remove downvote no matter how much you don't like the `shared_ptr` design. – Cheers and hth. - Alf Jan 23 '11 at 13:37
  • @Alf I do like shared_ptr design, but it does not makes what you suggest less dangerous or applicable in generic case. So if you want to use the technique you suggested you need to be super careful and understand in 1000% how shared_ptr works. So I'm sorry but I still think your anwer is wrong. – Artyom Jan 23 '11 at 13:41
  • @Artyom: your answer is incorrect. your dislike of `shared_ptr` design does not make my answer wrong. your answer, on the other hand, saying that that is impossible, is technically wrong. don't you understand that "impossible" != "possible"? – Cheers and hth. - Alf Jan 23 '11 at 14:25
4

A shared pointer can be shared by many things, you can't just take it from them all somehow. This is elaborated by Artyom and peoro.

One approach is to make a temporary auto_ptr, and release it from handling the pointer at the end of the scope. dalle outlines a first approach, but this suffers from lack of exception-safety (might accidentally delete), and it cannot protect you from accidentally passing it to a function that's going to transfer ownership (where the delete falls out of our hands).

We can make our own wrapper to avoid this, though:

template <typename T>
class auto_ptr_facade
{
public:   
    auto_ptr_facade(shared_ptr<T> ptr) :
    mPtr(ptr),
    mAuto(ptr.get())
    {}

    ~auto_ptr_facade()
    {
        // doesn't actually have ownership
        mAuto.release();
    }

    // only expose as const, cannot be transferred
    const auto_ptr<T>& get() const
    {
         return mAuto;
    }

    operator const auto_ptr<T>&() const
    {
         return get();
    }

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

    shared_ptr<T> mPtr;
    auto_ptr<T> mAuto;
};

Now you can treat a shared_ptr like a const auto_ptr, in a scope:

template <typename T>
void foo(shared_ptr<T> ptr)
{
    auto_ptr_facade<T> a(ptr);

    // use a
}
Community
  • 1
  • 1
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • 1
    @GMan: but, if it expires then the pointee is already deleted by ptr.reset() and result becomes a dangling pointer – Yakov Galka Jan 23 '11 at 11:45
  • 1
    Also in almost any useful case ptr will be a copy of the shared_ptr passed as a parameter: `try_make_auto_ptr(my_local_ptr)`, so it won't expire. – Yakov Galka Jan 23 '11 at 11:48
  • @ybungalobill: Oh, duh, haha. Sleepy. :) I've expanded on dalle's answer instead. – GManNickG Jan 23 '11 at 11:52
  • -1 In the current code `ptr` is passed by value. That nearly ensures that it isn't the only ref. Also, the code is needlessly inefficient by using a `weak_ptr`. Methinks original authors were Java-men. `shared_ptr::use_count` exists for both `boost::shared_ptr` and C++0x `std::shared_ptr`. Finally, the `reset` would tend to destroy the referred to object. Uh oh... – Cheers and hth. - Alf Jan 23 '11 at 11:57
  • @Gman: OK, entirely new approach. I remove my downvote! :-) In passing, *if you know* the type of the `shard_ptr`'s deleter function then you can really take ownership away. But via the public interface that's only possible when a custom destroy function was specified at creation. I remember arguing strenuously for keeping the `get_deleter` thing just for this. But it would have been nice if the default deleter func's type was available! Cheers, – Cheers and hth. - Alf Jan 23 '11 at 12:20
  • @Gman: it doesn't accept removal of downvote, sorry. It says the answer must be edited first. But it has been edited. – Cheers and hth. - Alf Jan 23 '11 at 12:21
  • @Alf: There's a timer. No worries, I see a typo. – GManNickG Jan 23 '11 at 12:25
3

Usually it's a bad idea, since both auto_ptr and shared_ptr take the ownership of your pointer (they'll care about destroying it and stuff, according to different policies).

Having two different objects holding the ownership for the same pointer will likely result in run time errors unless your doing it for some really good (and weird!) reasons.

peoro
  • 25,562
  • 20
  • 98
  • 150
2

Assuming you want to transfer ownership from a shared_ptr to auto_ptr, this is only possible when

  • The reference count of the shared_ptr is 1, and
  • the shared_ptr was originally created with a custom deleter function, and
  • you know the type of that deleter function.

Given that, here's how:

#include <iostream>
#include <boost/shared_ptr.hpp>     // boost::shared_ptr
#include <memory>                   // std::auto_ptr

typedef boost::shared_ptr<int>  IntSharedPtr;
typedef std::auto_ptr<int>      IntAutoPtr;

template< class Type >
void myCustomDeleter( Type* p )
{
    delete p;
}

IntSharedPtr newSharedInt()
{
    return IntSharedPtr( new int( 42 ), &myCustomDeleter<int> );
}

IntAutoPtr makeAutoFrom( IntSharedPtr& sp )
{
    struct Dummy
    {
        static void deleter( int* ) {}
    };

    typedef void (*DeleterFunc)( int* );

    if( sp.use_count() > 1 ) { return IntAutoPtr( 0 ); }
    DeleterFunc*    d   = boost::get_deleter<DeleterFunc>( sp );

    if( d == 0 ) { return IntAutoPtr( 0 ); }

    int* const  p   = sp.get();
    *d = &Dummy::deleter;
    sp.reset();
    return IntAutoPtr( p );
}

template< class T >
T& refTo( T const& r ) { return const_cast< T& >( r ); }

int main()
{
    IntAutoPtr  p( makeAutoFrom( refTo( newSharedInt() ) ) );

    std::cout << (p.get() == 0? "Failed" : "Worked" ) << std::endl;
}

Note: this technique isn't thread-safe.

Cheers & hth.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • -1 It is not thread safe and it is very dangerous and what is even more important it can't become thread-safe. I would strongly discourage from using such "tricks". – Artyom Jan 23 '11 at 13:01
  • @Artyom: it's the answer to the OP's question. You should not downvote an answer telling you of existence of X (when OP asked about X), just because you don't like X. Then downvote the question instead. – Cheers and hth. - Alf Jan 23 '11 at 13:03
  • The author explicitly asks if this is done **by design**, and yes, it is done by design. Almost every thing can be worked around, but this does not make this method reasonable. – Artyom Jan 23 '11 at 13:06
  • Also shared_ptr designed to be thread safe and this access violates its property. – Artyom Jan 23 '11 at 13:09
  • @Artyom: the OP asks whether it is "impossible by design". You answered "It is impossible by design", and that's incorrect. That's why I downvoted your answer: you answer is *incorrect*. I showed the possibility that is there by design. Which is correct answer. And yes, `get_deleter` is there by design. It also has other uses. – Cheers and hth. - Alf Jan 23 '11 at 13:11
  • You "solution" does not work in general case: it is not thread safe and it does not work with general deleter - so it is plain wrong :-) – Artyom Jan 23 '11 at 13:23
  • @Artyom: i've specified exactly the conditions for that code. the functionality is there by design. it proves your answer incorrect. stop bickering. – Cheers and hth. - Alf Jan 23 '11 at 13:39
  • Please add at least at the beginning of pre-conditions also: "it is not used from multiple threads or no weak_ptr is used" – Artyom Jan 23 '11 at 13:46
  • @Artyom: the bit about weak_ptr would be wrong, the bit about threads is already addressed (do read). just to repeat: your answer that the above is impossible, is incorrect. is that so hard to understand -- something that is possible, is not impossible? please, stop now. – Cheers and hth. - Alf Jan 23 '11 at 13:57
  • @Alf - I don't want to continue, but **given general case** it is impossible, **given very specific case** you suggest it seems to be possible, so the answer _possible_ or _impossible_ is conditional, yet in the general case the answer is _impossible_. – Artyom Jan 23 '11 at 14:03
  • @Artyom: please don't shout. your answer is incorrect: you say the above is impossible, while it is not only possible but a possibility that is designed-in. you do not mention anything about "general case" in your answer. if you had so qualified your statement, then it could have been correct. you can, in principle, learn from that. – Cheers and hth. - Alf Jan 23 '11 at 14:19
1

You should not do that, as auto_ptr takes ownership of the pointer.

But you can do it, but make sure you call release before you fall out of scope.

void foo(shared_ptr<Y> s)
{
    auto_ptr<Y> a(s.get());

    // use a

    a.release();
}

EDIT: The above solution is not exception safe. The following should work, combining a guard class with the guarantee that a const auto_ptr cannot be copied:

void bar(const auto_ptr<Y>& p);

struct as_const_auto_ptr
{
    explicit as_const_auto_ptr(const shared_ptr<Y>& p) : p(p), a(p.get()) {}
    ~as_const_auto_ptr() {a.release();}
    operator const auto_ptr<Y>&() {return a;}
    const shared_ptr<Y> p;
    auto_ptr<Y> a;
};

void foo(shared_ptr<Y> s)
{
    as_const_auto_ptr a(s);

    // use a.
    bar(a);
}
dalle
  • 18,057
  • 5
  • 57
  • 81