Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it dangerous to have a cast operator on a unique_ptr?

We have an extensive code base which currently uses raw pointers, and I'm hoping to migrate to unique_ptr. However, many functions expect raw pointers as parameters and a unique_ptr cannot be used in these cases. I realize I can use the get() method to pass the raw pointer, but this increases the number of lines of code I have to touch, and I find it a tad unsightly. I've rolled my own unique_ptr which looks like this:

template <class T>
class my_unique_ptr: public unique_ptr <T>
{
  public:

    operator T*() { return get(); };
};

Then every time I provide a my_unique_ptr to a function parm which expects a raw pointer, it automagically turns it into the raw pointer.

Question: Is there something inherently dangerous about doing this? I would have thought this would have been part of the unique_ptr implementation, so I'm presuming its omission is deliberate - does anyone know why?

like image 794
hunter Avatar asked Jun 21 '12 20:06

hunter


3 Answers

There's a lot of ugly things that can happen on accident with implicit conversions, such as this:

std::unique_ptr<resource> grab_resource() 
{return std::unique_ptr<resource>(new resource());}

int main() {
    resource* ptr = grab_resource(); //compiles just fine, no problem
    ptr->thing(); //except the resource has been deallocated before this line
    return 0; //This program has undefined behavior.
}
like image 181
Mooing Duck Avatar answered Oct 05 '22 18:10

Mooing Duck


It is the same as invoking the get on the unique_ptr<>, but it will be done automatically. You will have to make sure the pointer is not stored/used after the function returns (as unique_ptr<> will delete it when its lifetime ends).

Also make sure you don't call delete (even indirectly) on the raw pointer.

Yet another thing to make sure is that you do not create another smart pointer that takes ownership of the pointer (e.g. another uniqe_ptr<>) -- see delete note above

The reason for unique_ptr<> not doing the conversion for you automatically (and have you call get() explicitly) is to ensure you have control over when you access the raw pointer (to avoid the above issues that could happen silently otherwise)

like image 37
Attila Avatar answered Oct 05 '22 16:10

Attila


The main "danger" in providing an implicit conversion operator (to a raw pointer) on a unique_ptr stems from the fact that unique_ptr is supposed to model single-ownership semantics. This is also why it's not possible to copy a unique_ptr, but it can be moved.

Consider an example of this "danger":

class A {};

/* ... */

unique_ptr<A> a(new A);

A* a2 = a;
unique_ptr<A> a3(a2);

Now two unique_ptrs model single-ownership semantics over the object pointed to, and the fate of the free world -- nay, the universe -- hangs in the balance.

OK, I'm being a bit dramatic, but that's the idea.

As far as workarounds go, I would normally just call .get() on the pointer and be done with it, being careful to recognize a lack of ownership on what I just got().

Given that you have a large, legacy code-base that you are trying to migrate to using unique_ptr, I think your conversion wrapper is fine -- but only assuming you and your co-workers don't make mistakes in the future when maintaining this code. If that is a possibility you find likely (and I normally would because I'm paranoid), I would try to retrofit all existing code to call .get() explicitly instead of providing an implicit conversion. The compiler will happily find all the instances for you where this change needs to be made.

like image 23
John Dibling Avatar answered Oct 05 '22 17:10

John Dibling