Suppose I have an object which is managed by an std::unique_ptr
. Other parts of my code need to access this object. What is the right solution to pass the pointer?
If the ownership of the managed object is not being transferred (and because it's a unique_ptr
, ownership cannot be shared) then it's more correct to separate the logic in the called function from the concept of ownership. We do this by calling by reference.
This is a convoluted way of saying:
Given:
std::unique_ptr thing_ptr;
to change the Thing:
// declaration
void doSomethingWith(Thing& thing);
// called like this
doSomethingWith(*thing_ptr);
to use the Thing without modifying it.
// declaration
void doSomethingWith(const Thing& thing);
// called like this
doSomethingWith(*thing_ptr);
The only time you'd want to mention the unique_ptr
in the function signature would be if you were transferring ownership:
// declaration
void takeMyThing(std::unique_ptr p);
// call site
takeMyThing(std::move(thing_ptr));
You never need to do this:
void useMyThing(const std::unique_ptr& p);
The reason that this would be a bad idea is that if confuses the logic of useMyThing with the concept of ownership, thus narrowing the scope for re-use.
Consider:
useMyThing(const Thing& thing);
Thing x;
std::unique_ptr thing_ptr = makeAThing();
useMyThing(x);
useMyThing(*thing_ptr);
Update:
Noting the update to the question - storing (non-owning) references to this object.
One way to do this is indeed to store a pointer. However, pointers suffer from the possibility of a logic error in that they can legally be null. Another problem with pointers is that they do not play nicely with std:: algorithms
and containers - requiring custom compare functions and the like.
There is a std::-compliant
way to do this - the std::reference_wrapper<>
So rather than this:
std::vector my_thing_ptrs;
do this:
std::vector> my_thing_refs;
Since std::reference_wrapper
defines an operator T&
, you can use the reference_wrapped
object in any expression that would expect a T
.
for example:
std::unique_ptr t1 = make_thing();
std::unique_ptr t2 = make_thing();
std::unique_ptr t3 = make_thing();
std::vector> thing_cache;
store_thing(*t1);
store_thing(*t2);
store_thing(*t3);
int total = 0;
for(const auto& t : thing_cache) {
total += value_of_thing(t);
}
where:
void store_thing(const Thing& t) {
thing_cache.push_back(std::cref(t));
}
int value_of_thing(const Thing& t) {
return ;
}