I have an instance of class Foo
that will be passed a smart pointer to a dependency object. This may be a unique_ptr
, if the caller wants to transfer ownership of the object to the Foo
instance, or a shared_ptr
if the caller wants to share the object with the Foo
instance and other things. Perhaps one day it might even accept a weak_ptr
so that it can make use of a shared object, for as long as it exists (EDIT: but let's ignore weak_ptr
, it clearly complicates things).
My question is, what is the idiomatic way to manage a generic smart pointer in this way?
One idea that comes to mind is to use a shared_ptr
for storage, and overloaded functions to load it:
#include <iostream>
#include <memory>
class Bar {};
class Foo {
public:
void store(std::unique_ptr<Bar> p) { p_ = std::move(p); }
void store(std::shared_ptr<Bar> p) { p_ = std::move(p); }
// void store(std::weak_ptr<Bar> p) { p_ = p.lock(); } // don't worry about weak_ptr
private:
std::shared_ptr<Bar> p_; // a shared_ptr can store a unique_ptr, but irreversibly
};
int main() {
Foo f {};
// pass ownership of ub to f
auto ub = std::make_unique<Bar>();
f.store(std::move(ub));
// create shared ownership of sb, share with f
auto sb = std::make_shared<Bar>();
f.store(sb);
}
I suppose an alternative could be to use a templated class, to determine the storage type at compile time, but in my case I need the Foo
instance to potentially accept any of the two pointers at run-time. Also, once the pointer is stored as a shared_ptr
, it is not possible to return it to a unique_ptr
.
Is there a better approach than two overloads and conversion to a stored shared_ptr
?
I'm going to attempt to rephrase my question below, to make it clearer what I'm trying to do. Unfortunately this part is now a "design question" and therefore can't be asked in its own question on SO.
I have a script-driven system that creates properties of a hierarchy of Object
s - think 3D objects with associated materials. As the script is parsed, the object hierarchy (tree) is built, and materials are created and associated with these objects. Some materials are solely owned by a single Object, some are shared between multiple objects, and some may be weak references to other materials (if it helps, drop this requirement, I'm more concerned about unique/shared ownership).
The materials are created with a factory function, that currently returns a unique_ptr<Material>
. I chose this because Scott Meyers suggests that it is the most versatile type to return from a factory function, because it doesn't require the factory function to know about any final ownership strategy. The caller of the factory function can change this unique_ptr
into anything else, like a shared_ptr
or even a weak_ptr
, depending on how it intends ownership of this Material to be handled.
Then this is passed to an Object, with the caller determining how the Object will manage this material. Perhaps it will be given as sole owner, or perhaps it will be shared between multiple Objects. So an Object needs to be able to accept, and store, either a unique_ptr
or shared_ptr
. Once it has this pointer, it doesn't actually care what it is, it's really just for Material clean-up once the Object is destroyed.
Comments suggest that this is an unusual pattern - and if so, I'm keen to hear about alternative designs that might accomplish the same thing - which I could imagine could be described as "lifetime management of heap-allocated factory-created dependencies that are injected into another object".