Marc.2377 Marc.2377 - 20 days ago 5
C++ Question

Is implicit conversion from unique_ptr to raw pointer supported?

I was reading Effective C++ 3rd Edition. In page 70, the author says:


Like virtually all smart pointer classes,
tr1::shared_ptr
and
auto_ptr
also overload the pointer dereferencing operators (
operator->
and
operator*
), and this allows implicit conversion to the underlying raw pointers (...)


He then shows an example with
shared_ptr
(which was part of
tr1
at the time) featuring implicit conversion based on a class named
Investment
:

shared_ptr<Investment> pi1();
bool taxable1 = !(pi1->isTaxFree());
^implicit conversion

shared_ptr<Investment> pi2();
bool taxable2 = !((*pi2).isTaxFree());
^implicit conversion


Well, I have since then written a few test cases with
unique_ptr
and they hold up.

I also found out about
unique_ptr
supporting arrays
and
shared_ptr
is also going to
(see note). However, in my testing, implicit conversion does not seem to work for smart pointers around arrays.

Example: I wanted this to be valid...

unique_ptr<int[]> test(new int[1]);

(*test)[0] = 5;


but it is not, according to my compiler (Visual C++ 2015 Update 3).

Then from doing a search, I found some evidence suggesting that implicit conversion isn't supported at all... like this one for instance: https://herbsutter.com/2012/06/21/reader-qa-why-dont-modern-smart-pointers-implicitly-convert-to.

At this point I am in doubt. Is it supported (by the Standard), or is it not?



Note: The book might be a bit outdated on this topic, since the author also says on page 65 that "there is nothing like
auto_ptr
or
tr1::shared_ptr
for dinamically allocated arrays, not even in TR1".

Answer

Well, here's the thing. There is no implicit conversion to the underlying pointer, you have to call a specific get member function (it's a theme in the standard library, think std::string::c_str).

But that's a good thing! Implicitly converting the pointer can break the guarantees of unique_ptr. Consider the following:

std::unique_ptr<int> p1(new int);
std::unique_ptr<int> p2(p1);

In the above code, the compiler can try to pass p1s pointee to p2! (It won't since this call will be ambiguous anyway, but assume it wasn't). They will both call delete on it!

But we still want to use the smart pointer as if it was a raw one. Hence all the operators are overloaded.


Now let's consider your code:

(*test)[0] = 5;

It calls unique_ptr::operator* which produces an int&1. Then you try use the subscript operator on it. That's your error.
If you have a std::unique_ptr<int[]> than just use the operator[] overload that the handle provides:

test[0] = 5;

1 As David Scarlett pointed out, it shouldn't even compile. The array version isn't supposed to have this operator.

Comments