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> 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<Thing> p);
// call site
takeMyThing(std::move(thing_ptr));
You never need to do this:
void useMyThing(const std::unique_ptr<Thing>& 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> 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<Thing*> my_thing_ptrs;
do this:
std::vector<std::reference_wrapper<Thing>> my_thing_refs;
Since std::reference_wrapper<T>
defines an operator T&
, you can use the reference_wrapped
object in any expression that would expect a T
.
for example:
std::unique_ptr<Thing> t1 = make_thing();
std::unique_ptr<Thing> t2 = make_thing();
std::unique_ptr<Thing> t3 = make_thing();
std::vector<std::reference_wrapper<const Thing>> 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 <some calculation on t>;
}