0

If function Foo() transfers ownership of std::unique_ptr to function Bar() and say Bar() throws an exception, object contained in std::unique_ptr will get destroyed.

How can one handle a case where Foo() might want to keep the ownership with itself in such cases.

class A
{
public:
    std::unique_ptr<SomeData> getSomeData(...);
};

class B
{
public:
    pushToQ(std::unique_ptr<SomeData>);
    doSomething()
    ...
    popFromQ();
    ...
};

Now if B::pushToQ() throws QueueFullException I will loose the data generated by getSomeData() which I might not want.

David G
  • 94,763
  • 41
  • 167
  • 253
Rashida
  • 91
  • 1
  • 2
  • 11

4 Answers4

6

You're either transferring ownership - or you're not. If you want to transfer, then you shouldn't care if Bar can throw and kill your object:

// i do not care what bar does with ptr, i am done with it
bar(std::move(ptr));

If you might want to retain ownership maybe, then transferring ownserhip is the wrong solution. Either you want to pass your unique_ptr by reference, or maybe just pull out the raw pointer, or maybe even just use a shared_ptr. It depends on your use-case. But there's no half-way ownership transfer.

Here are some examples. Which is preferred is entirely up to you:

bar(std::unique_ptr<Data>& ptr) {
    // not transferring, caller still owns the data
    something_that_might_throw();
    something_that_might_not();

    // ok got here?
    std::unique_ptr<Data> myPtr = std::move(ptr);
    // MINE NOW!!!
}

The above is "the ONLY exception safe solution to moving a unique_ptr into a container in an exception safe manner with a strong guarantee that the system won't be corrupted" (from MGetz)

Or:

bar(Data* ptr) {
    // it really doesn't matter, caller always owns the data
    // just don't go doing something like this

    std::unique_ptr<Data> awful_idea(ptr);

    // now two separate people both think they own the data
    // and will both try to delete it. That'll work the first time...
}

Or a strictly better version so you can't mess it up unless you try really hard

bar(Data& data) {
    // well clearly the caller owns it
}

Or:

bar(std::shared_ptr<Data> ptr) {
    // we both share the data
    // it won't get deleted until both bar() and caller delete it
}
Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977
  • @Mgetz can you please explain on how I can achieve this. Thanks. – Rashida Nov 13 '14 at 17:33
  • @Mgetz how would an exception in the `shared_ptr` case cause data loss? – Barry Nov 13 '14 at 17:43
  • Related: [GotW #91 Solution: Smart Pointer Parameters](http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/) – Mgetz Nov 13 '14 at 17:44
  • @Barry the solution you don't like is the ONLY exception safe solution to moving a `unique_ptr` into a container in an exception safe manner with a strong guarantee that the system won't be corrupted. Passing the `unique_ptr` by value means that the ownership is transferred on call, which is not desired if the function might throw and should ONLY be used if the function is `nowthrow` – Mgetz Nov 13 '14 at 17:49
  • 1
    @Barry because I'm lazy and 99% of yours is spot on – Mgetz Nov 13 '14 at 18:07
  • @Mgetz Ha! With the amount of suggestions you've thrown around here, I'm not sure I buy lazy :-P – Barry Nov 13 '14 at 18:11
2

Then don't transfer the ownership.

Pass a SomeData& into Bar(), instead.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • use the former to indicate ownership transfer may occur and the latter to indicate it won't occur – Mgetz Nov 13 '14 at 17:25
  • or just a `SomeData&` and deference the `unique_ptr` if it's known to be valid. – PeterT Nov 13 '14 at 17:27
  • I do not see how that solves his issue. He wants to push object into queue, how is passing by reference is solving the issue? Make a copy and push it into queue? – Slava Nov 13 '14 at 17:41
  • @Slava he wants to pass the object without giving up ownership as far as I can tell, so how does this not do it? – PeterT Nov 13 '14 at 17:53
  • 1
    @PeterT function name is `pushToQ()` exception possible thrown is QueueFullException, does not ring a bell? – Slava Nov 13 '14 at 17:56
  • @Slava hm, well the only thing that comes to mind then would be passing an rvalue I guess. I just don't know how to communicate whether ownership was take or not. – PeterT Nov 13 '14 at 18:27
1

You can use std::shared_ptr instead of std::unique_ptr and release your instance if pushToQ() succeded, keep your copy otherwise.

Slava
  • 43,454
  • 1
  • 47
  • 90
  • Seems like a blunt instrument. You're weakening your entire design's ownership contracts as a cheap way to work around a resolvable problem! – Lightness Races in Orbit Nov 13 '14 at 17:28
  • @LightnessRacesinOrbit At least it is obvious what is happening looking into code. Passing `std::unique_ptr` as a reference hides that fact and makes code less readable. So it is at least questionable which solution is cheaper. – Slava Nov 13 '14 at 17:33
  • Yeah, granted, that was a bit of a misstep. I've removed it. – Lightness Races in Orbit Nov 13 '14 at 17:38
1

In the called function, store the unique_ptr in the thrown exception object. If the callee - or some other function up the call stack - wants to save it, it can then be extracted. If not, it will be safely destroyed.

Casey
  • 41,449
  • 7
  • 95
  • 125