I'm pretty new to C++11's smart pointers, and I'm trying to use them effectively in a project. In my project, I have a lot of functions that take a const reference to a vector
of unique_ptr
, do some computations on it, and place some results in a return parameter, like this:
void computeCoefficients(const vector<unique_ptr<Scalar>>& roots,
vector<unique_ptr<Scalar>>& coeffs) {
...
}
I'm using unique_ptr
because the procedure calling all these functions is the sole owner of the objects in the vector
, and the functions are just "borrowing" the objects in order to read them as input.
Now I'm trying to write a function that does computations on different subsets of the vector
it receives, and in order to do that it needs to have different "versions" of the vector
containing those subsets in order to pass to yet another function that takes a vector<unique_ptr<Scalar>>
as input. But the only way to get a subset of a vector is to make a copy of it - which is a problem because unique_ptr
s can't be copied. I'd like the code to look something like this:
void computeOnSet(const vector<unique_ptr<Scalar>>& set, unique_ptr<Scalar>& output) {
...
}
void computeOnAllSubsets(const vector<unique_ptr<Scalar>>& set, vector<unique_ptr<Scalar>>& outputs) {
for(int i = 0; i < input.size(); i++) {
auto subset = vector<unique_ptr<Scalar>>(set.begin(), set.begin()+i);
subset.insert(subset.end(), set.begin()+i+1, set.end();
computeOnSubset(subset, outputs.at(i));
}
}
Of course that doesn't work. I could make it work if I replaced the unique_ptr
s with shared_ptr
s, but that has two problems:
computeOnSubsets
function, and I'm not; the caller is still the sole owner. (I read that shared_ptr
means you're sharing ownership with everything that has a copy of it).vector<shared_ptr<Scalar>>
. All I want to do is make a temporary, read-only copy of a pointer, for the sole purpose of making temporary, read-only sub-vectors. Is there any way to do this? weak_ptr
sounds like what I need (non-owning temporary pointer), but it can only be used with shared_ptr
.
I'm using unique_ptr because the procedure calling all these functions is the sole owner of the objects in the vector, and the functions are just "borrowing" the objects in order to read them as input.
Since the computing functions are not owning the pointed objects, just observing their state and making computations, you should pass them a vector of observing pointers (in this case, regular raw pointers), instead of a vector of unique_ptr
s.
Since computeOnAllSubsets()
and computeOnSet()
are not responsible for the lifetime of the Scalar
objects, they should not even acquire their ownership - in other words, they should not receive the owning unique_ptr
s.
After all, it is guaranteed by the logic of your program that those functions won't be receiving dangling references, because the owning function won't destroy its vector before it has performed all the necessary computations. This is supported directly by what you are writing:
All I want to do is make a temporary, read-only copy of a pointer, for the sole purpose of making temporary, read-only sub-vectors. Is there any way to do this?
Just pass a vector of raw pointers to your computing functions. Given a unique_ptr
, you can get access to the encapsulated raw pointer by calling the member function get()
:
std::unique_ptr<Scalar> pS // ... initialized somehow
Scalar* pScalar = pS.get();
As an alternative to raw pointers, you could use std::reference_wrapper
for dispatching observing references. Especially during the process of refactoring a legacy code base where raw pointers are used for manual memory management, this would make it clear that ownership of the referenced objects belongs somewhere else.
Notice, however, that in Modern C++ a raw pointer is most often a synonym of observing pointers, so the above distinction is not really meaningful in a generalized context. The use case for which std::reference_wrapper
is fundamental is when you want to pass objects by reference to some function template that accepts its arguments by value (std::bind()
being a typical example).
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