I want to pass a unique_ptr
to a helper function, and I want to make sure that the helper function neither modifies the pointer, nor the pointed object. Without the unique_ptr
, the solution is to have
void takePtr(AClass const * const aPtr) { // Do something with *aPtr. // We cannot change aPtr, not *aPtr. }
(Well, technically, AClass const * aPtr
is enough.) And I can call this with
AClass * aPtr2 = new AClass(3); takePtr(aPtr2);
I want to instead use unique_ptr
, but cannot figure out how to write this. I tried
void takeUniquePtr(unique_ptr<AClass const> const & aPtr) { // Access *aPtr, but should not be able to change aPtr, or *aPtr. }
When I call this with
unique_ptr<AClass> aPtr(new AClass(3)); takeUniquePtr(aPtr);
it does not compile. The error I see is
testcpp/hello_world.cpp:141:21: error: invalid user-defined conversion from ‘std::unique_ptr<AClass>’ to ‘const std::unique_ptr<const AClass>&’ [-fpermissive]
Shouldn't the conversion from unique_ptr<AClass>
to unique_ptr<AClass const>
be automatic? What am I missing here?
By the way, if I change unique_ptr<AClass const> const & aPtr
to unique_ptr<AClass> const & aPtr
in the function definition, it compiles, but then I can call functions like aPtr->changeAClass()
, which I don't want to allow.
boost::shared_ptr<Bar const> prevents modification of the Bar object through the shared pointer. As a return value, the const in boost::shared_ptr<Bar> const means that you cannot call a non-const function on the returned temporary; if it were for a real pointer (e.g. Bar* const ), it would be completely ignored.
A unique_ptr does not share its pointer. It cannot be copied to another unique_ptr , passed by value to a function, or used in any C++ Standard Library algorithm that requires copies to be made. A unique_ptr can only be moved.
std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. The object is disposed of, using the associated deleter when either of the following happens: the managing unique_ptr object is destroyed.
It can be assigned: class owner { std::unique_ptr<someObject> owned; public: owner() { owned=std::unique_ptr<someObject>(new someObject()); } };
Smart pointers are for managing ownership and lifetime, they allow us (amongst other things) to safely transfer ownership around the various parts of our code.
When you pass a const unique_ptr<T>&
to a function (irrelevant of whether T
is const
or not), what it actually means is that the function promises to never modify the unique_ptr
itself (but it could still modify the pointed-to object if T
is not const
) ie. there will be no possible transfer of ownership whatsoever. You're just using the unique_ptr
as a useless wrapper around a naked pointer.
So, as @MarshallClow suggested in a comment, you should just get rid of the wrapper and pass either a naked pointer or a direct reference. What's cool with this is that your code is now semantically clear (your function's signature clearly states that it does not mess with ownership, which was not immediately obvious with a const unique_ptr<...>&
) and it solves your "constification" problem at the same time!
Namely:
void someFunction(const AClass* p) { ... } std::unique_ptr<AClass> ptr(new AClass()); someFunction(ptr.get());
Edit: to address your secondary question "why won't the compiler let me ... cast unique_ptr<A>
to unique_ptr<A const>
?".
Actually, you can move a unique_ptr<A>
to a unique_ptr<A const>
:
std::unique_ptr<A> p(new A()); std::unique_ptr<const A> q(std::move(p));
But as you can see this means a transfer of ownership from p
to q
.
The problem with your code is that you're passing a (reference to) unique_ptr<const A>
to a function. Since there is a type discrepancy with unique_ptr<A>
, to make it work the compiler needs to instantiate a temporary. But unless you transfer ownership manually by using std::move
, the compiler will try to copy your unique_ptr
and it can't do that since unique_ptr
explicitly forbids it.
Notice how the problem goes away if you move the unique_ptr
:
void test(const std::unique_ptr<const int>& p) { ... } std::unique_ptr<int> p(new int(3)); test(std::move(p));
The compiler is now able to construct a temporary unique_ptr<const A>
and move the original unique_ptr<A>
without breaking your expectations (since it is now clear that you want to move, not copy).
So, the root of the problem is that unique_ptr
only has move semantics not copy semantics, but you'd need the copy semantics to create a temporary and yet keep ownership afterwards. Egg and chicken, unique_ptr
just isn't designed that way.
If you now consider shared_ptr
which has copy semantics, the problem also disappears.
void test(const std::shared_ptr<const int>& p) { ... } std::shared_ptr<int> p(new int(3)); test(p); //^^^^^ Works!
The reason is that the compiler is now able to create a temporary std::shared_ptr<const int>
copy (automatically casting from std::shared_ptr<int>
) and bind that temporary to a const
reference.
I guess this more or less covers it, even though my explanation lacks standardese lingo and is perhaps not as clear as it should be. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With