8

We all know that we can easily convert unique_ptr to shared_ptr in C++. But what if I have made such a conversion :-

unique_ptr<X> u=make_unique<X>();    // X is some class
shared_ptr<X> s=move(u);      // this works of course

And now i want to transfer the ownership of the pointer in s back to u. Sadly there is no release() function in shared_ptr like in unique_ptr else I could have dont something like this :-

u.reset(s.release());

Moreover this also fails to work :-

u.reset(s.get());

Can anyone suggest me how to convert shared_ptr to unique_ptr or atleast release the pointer owned by shared_ptr ?

Filip Roséen - refp
  • 62,493
  • 20
  • 150
  • 196
Anwesha
  • 728
  • 1
  • 7
  • 18
  • I think there is no such way where you can do the back conversion or release the ownership of shared_ptr – Ankit Acharya Oct 10 '15 at 19:50
  • 1
    Obligatory "Why do want to do that?" When you already have a `shared_ptr`, the `unique_ptr` does not have any advantage afaics. – Baum mit Augen Oct 10 '15 at 19:54
  • 1
    Once the pointer is given to `shared_ptr`, it could be further shared with more instances of `shared_ptr`. Releasing just one of them wouldn't help any. I suspect that's why there's no mechanism to do that. – Igor Tandetnik Oct 10 '15 at 19:54
  • @BaummitAugen, Consider a library method that accepts only unique_ptr – Andrey Nasonov Oct 10 '15 at 19:55
  • 1
    @AndreyNasonov Well, presumably, that method accepts `unique_ptr` because it takes ownership of the object. But if you only have `shared_ptr` in hand, that ownership is not yours to give; others may share it, too, and you have no way to yank it away from them. – Igor Tandetnik Oct 10 '15 at 19:58
  • 2
    @IgorTandetnik, I agree that this should not occur in well designed system. But what if `shared_ptr` has exactly one owner at the moment? – Andrey Nasonov Oct 10 '15 at 20:00
  • @AndreyNasonov If you have a way to guarantee that, then why do you trip through a `shared_ptr` in the first place? Anyway, if you are somehow painted into a corner and it's time for desperate measures, one way would be a custom deleter that could be told, via a side channel, to become a no-op. Then you `get()` and `reset()`, ending up with a raw pointer. – Igor Tandetnik Oct 10 '15 at 20:05
  • If you *really* want to do something like this (for whatever obscure reason), just do a `make_unique(std::move(*sharedptr));` – Baum mit Augen Oct 10 '15 at 20:09
  • Gentlemen, I m looking for an easy way to apply static_cast & dynamic_cast on unique_ptr. As shared_ptr has static_pointer_cast & dynamic_pointer_cast , so I was thinking of converting unique_ptr to shared_ptr, apply the cast & then do the back conversion – Anwesha Oct 10 '15 at 20:10
  • 1
    @Anwesha, When you apply static and dynamic casts to `shared_ptr`, you create a new `shared_ptr` object and increases the reference count. But you can simply apply casts directly to `unique_ptr.get()`. – Andrey Nasonov Oct 10 '15 at 20:23
  • @Anwesha do not add additional questions to a question after it has received answers, instead create a new one where you explicitly state your intent. The question was clearly written as it was, if you have follow-up questions; take them elsewhere. – Filip Roséen - refp Oct 11 '15 at 11:29

3 Answers3

18

You should not do that!

The standard library does not really facilitate such a move, going from std::unique_ptr to std::shared_ptr is always safe (because of the semantics of the two) — but the opposite is dangerous since there might be more owners of the resource than the particular std::shared_ptr you would like to move from.


No, really — do not do that!

I should probably tell you one more time, but I will assume that you are somewhat grown up and can stand accountable for your own decisions.


Ey, come on.. sure there is a hack for this?

If you would like to hack together a solution that would not be undefined-behavior unless the std::unique_ptr goes out of scope and you still have std::shared_ptrs that directly or indirectly end up using the resource.. you would probably end up with something like:

#include <memory>
namespace hack {
  struct conditional_deleter {
    void  disable () { _do_delete = false; }

    template<class T>
    void operator()(T* p) {
      if (_do_delete)
        delete p;
    }   

    bool _do_delete = true;
  };  
}
int main () {
  std::unique_ptr<int> up (new int (123));
  std::shared_ptr<int> sp (up.release (), hack::conditional_deleter {});
  std::shared_ptr<int> sb (sp);

  std::get_deleter<hack::conditional_deleter> (sp)->disable (); 
  std::unique_ptr<int> ub (sp.get ()); // see the name of this variable?
}                                      // can I get a ticket to UB-land, please?

WARNING

The above is far from recommended praxis, if you ever find yourself in a situation where you need such thing you should set fire to your workstation—probably your whole house—and work on a new program design.

Filip Roséen - refp
  • 62,493
  • 20
  • 150
  • 196
  • 2
    The only safe way is to subscribe to a static deletion notifier. The notifier could be signalled by the custom deleter of the shared pointer, and then deliver the unique pointer via a subscription callback.... A lot of work for no gain. – Richard Hodges Oct 10 '15 at 21:31
  • @RichardHodges certainly, and really; the only safe way is to implement a structure that does what *std::shared_ptr* does internally, but also handles *std::unique_ptr* handles (with a custom deleter) - or through a proxy that also does reference-counting (ie. reinventing the wheel). You got a **+1** for that comment. – Filip Roséen - refp Oct 10 '15 at 21:33
5

As everyone has mentioned, you cannot convert shared_ptr to unique_ptr because more than one shared_ptr might be owning the object. Hence there is no release() function for shared_ptr. Had there been one & you would have used release() on one shared_ptr object when more than one of such would be sharing a resource then there would be ambiguity for other shared_ptr pointers as to whether they own the resource or not.

However, because your actual problem was regarding the static& dynamic casting of unique_ptr (as you have mentioned in your comments) so I would like to add that you needn't actually use shared_ptr for casting unique_ptr & here I show you how you can do so very simply :-

void dynamic_unique_cast (unique_ptr<Parent> &pa)
{
    unique_ptr<Child> pb;    
    Parent *p=pa.release();   // release ownership
    try
    {
       pb.reset(dynamic_cast<Child*>(p));  // perform casting
       if (pb==nullptr)
       throw runtime_error {"nullptr exception"};
       cout<<"dynamic_cast successful\n\n";
       pa.reset(pb.release());  // if casting is successful then `pb` returns ownership back to `pa`
    }
    catch (exception &e)
    {
       cout<<"dynamic_cast unsuccessful: "<<e.what()<<"\n\n";
       pa.reset(p);  // if casting fails then `p` returns ownership to `pa`
    }
}
void static_unique_cast (unique_ptr<Parent> &pa)
{
    unique_ptr<Child> pb;
    Parent *p=pa.release();
    try
    {
       pb.reset(static_cast<Child*>(p));
       if (pb==nullptr)
       throw runtime_error {"nullptr exception"};
       show(pb);
       cout<<"static_cast successful\n\n";
       pa.reset(pb.release());
    }
    catch (exception &e)
    {
       cout<<"static_cast unsuccessful: "<<e.what()<<"\n\n";
       pa.reset(p);
    }
}

The above code works pretty well & will definitely solve your problem. If you have any ambiguity then feel free to comment.

Ankit Acharya
  • 2,833
  • 3
  • 18
  • 29
  • This code leaks resources if the dynamic cast fails, and does not take account of the custom deleter in a unique pointer. – Richard Hodges Oct 11 '15 at 09:43
  • This does not answer the question, if this is what *OP* was looking for a new question should be created - and this answer posted there. (flagged as *not an answer*) – Filip Roséen - refp Oct 11 '15 at 09:43
  • 2
    @FilipRoséen-refp: It is true that this does not answer the original question. OTOH it turns out (judging from the comments) that the OP was just facing an XY problem. This post does answer the real problem (X) the OP was trying to solve, but fails to mention it. I agree that without reading the last comment (which is not even expanded by default) it's nearly impossible to understand how this answer relates to the question, but I appreciate this answer for addressing the X rather than the Y. – Andy Prowl Oct 11 '15 at 11:18
  • @RichardHodges I have tried this program and it doesn't leak resources even when dynamic casting fails. You can give a try yourself. If your program doesn't support then comment here or mail me at ankitacharya96@gmail.com & i will send you the full code I was working with – Ankit Acharya Oct 11 '15 at 11:25
  • @AndyProwl A new question should be created where such an appropriate answer can be posted; questions are asked in the question, not in the comments. – Filip Roséen - refp Oct 11 '15 at 11:25
  • 1
    @FilipRoséen-refp And questions which already have answers should be [closed as duplicates](http://stackoverflow.com/questions/15337461/move-ownership-from-stdshared-ptr-to-stdunique-ptr), rather than being answered again. Ankit bothered to actually understand what the OP's problem was. I'm only saying I appreciate his answer for this. – Andy Prowl Oct 11 '15 at 11:48
  • @AndyProwl I will then close the question as a dupe when I get access to a computer, I would recommend OP to post a new question that fully states the intended usage (where answers such as this one is far more appropriate). – Filip Roséen - refp Oct 11 '15 at 12:26
  • @AndyProwl now I see that this has already been done, good! – Filip Roséen - refp Oct 11 '15 at 12:27
  • 1
    @AnkitAcharya you're right. The catch clause sorts it out, my bad. That just leaves the custom deleter to be sorted out. See below for that. – Richard Hodges Oct 11 '15 at 15:16
  • My pleasure @RichardHodges – Ankit Acharya Oct 12 '15 at 14:34
2

OK, having looked at the comments I now realise that what you wanted was a version of dynamic_pointer_cast for unique_ptrs.

Remembering that unique_ptrs are well... unique, here is the answer:

Note that this answer may appear at first glance to be un-necessarily complex, but I think it's important to remember that unique_ptrs can have custom deleters. If we dynamic cast the unique_ptr into new one, the deleter must follow, but without the insertion of a translating shim, the pointer passed to the new unique_ptr's deleter would be of the wrong type.

This code not only dynamically moves ownership to a new pointer type, but also wires in the correct reverse pointer cast so that the object can be deleted on the correct interface by the correct deleter when the moved-to unique_ptr finally goes out of scope.

#include <iostream>
#include <memory>

template<class Dest, class Source, class Deleter>
auto
make_proxy_deleter(std::unique_ptr<Source, Deleter>& source)
{
    return [original = source.get_deleter()](Dest* p) {
        original(dynamic_cast<Source*>(p));
    };
}

template<class Dest, class T, class Deleter>
auto
dynamic_cast_unique(std::unique_ptr<T, Deleter>&& source)
{
    auto proxy_deleter = make_proxy_deleter<Dest>(source);
    auto p = dynamic_cast<Dest*>(source.get());
    if (!p) {
        return std::unique_ptr<Dest, decltype(proxy_deleter)>(nullptr,
                                                              std::move(proxy_deleter));
        // or... throw std::invalid_argument("not convertible");
    }

    return std::unique_ptr<Dest, decltype(proxy_deleter)>(dynamic_cast<Dest*>(source.release()),
                                                          std::move(proxy_deleter));
}

struct A {
    virtual ~A() {};
};
struct B {
    virtual ~B() {};
};
struct C: A, B {};


using namespace std;

auto main() -> int
{
    auto pa = make_unique<C>();
    auto pb = dynamic_cast_unique<B>(std::move(pa));
    return 0;
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • good one ! but u u seem to be using auto everywhere (even for int main()) !!!11 – Anwesha Oct 11 '15 at 08:49
  • Trailing return types are useful on so many places that I just use them out of force of habit. The use of auto in the template code reduces the amount of typing by an order of magnitude. – Richard Hodges Oct 11 '15 at 09:39