navark navark - 3 months ago 24
C++ Question

shared_ptr and unique_ptr conversion

I'm in the situation where I'm not sure which type of smart pointer to use because I'm not sure of all the use cases for my class. I could just use shared pointers but I'm not fond of the idea of passing shared pointers everywhere in my code when I don't necessarily need shared ownership. This article by Herb Sutter says that when in doubt, use unique_ptr and convert to shared_ptr when you have to. This is what I'd like to do but I'm unclear as to how this is supposed to be done, consider this example:

class Example
{
public:
Example(): _ptr(std::make_unique<Node>()) {}

std::unique_ptr<Node>& getPtr()
{
return _ptr;
}

private:
// I am unsure if I will eventually need shared ownership or not
std::unique_ptr<Node> _ptr;
};

Example* example = new Example();


// Some function somewhere
void f()
{
// I've decided I need shared ownership, converting
std::shared_ptr<Node> ptr(std::move(example->getPtr()));

// Oops, example is no longer valid...
}


If someone has a better idea of how to deal with situations like this I'd be glad to hear it.

jxh jxh
Answer

I think you are asking a kind of optimization question. You want Example to use unique_ptr because it has simpler and more efficient semantics (paraphrasing your referenced article). But, when the need arises, you wish to allow the pointer to be converted to shared_ptr.

Example should simply provide an interface for that, and itself needs to convert from unique_ptr to shared_ptr, when its user invokes that interface. You could use state pattern to capture whether the instance is in unique_ptr mode or shared_ptr mode.

class Example
{
    struct StateUnique;
    struct StateShared;
    struct State {
        virtual ~State () {}
        virtual Node * getPtr () = 0;
        virtual StateUnique * is_unique () { return 0; }
        virtual StateShared * is_shared () { return 0; }
    };
    struct StateUnique : State {
        StateUnique () : _ptr(std::make_unique<Node>()) {}
        Node * getPtr () { return _ptr.get(); }
        StateUnique * is_unique () { return this; }
        std::unique_ptr<Node> _ptr;
    };
    struct StateShared : State {
        StateShared (StateUnique &u) : _ptr(std::move(u._ptr)) {}
        Node * getPtr () { return _ptr.get(); }
        StateShared * is_shared () { return this; }
        std::shared_ptr<Node> _ptr;
    };

    public:
        Example(): _state(std::make_unique<StateUnique>()) {}
        Node & getNode() { return *_state->getPtr(); }
        std::shared_ptr<Node> & getShared()
        {
            if (!_state->is_shared()) {
                StateUnique *s = _state->is_unique();
                _state = std::make_unique<StateShared>(*s);
            }
            return _state->is_shared()->_ptr;
        }

    private:
        std::unique_ptr<State> _state;
};