user1876942
user1876942

Reputation: 1501

convert Reference to shared pointer

I have a function like

void Element::setNodes(const BaseClass& input0, const BaseClass& input1)

This is called by passing a derived class.

setInputNodes(DerivedClass1, DerivedClass2)

The trouble I have is that I want to store the nodes in a vector. I have this

std::vector<std::shared_ptr<BaseClass>> m_inputNode;

and the function as

void Element::setNodes(const BaseClass& input0, const BaseClass& input1)
{
m_inputNode.push_back(input0);
m_inputNode.push_back(input1);
}

This doesn't work and I have to store it as a pointer otherwise I experience object slicing. Will I need to change the API and pass pointers? I want to change as little as possible.

Upvotes: 2

Views: 3017

Answers (3)

cdonat
cdonat

Reputation: 2822

You have to take the pointers to your Elements:

void Element::setNodes(const BaseClass& input0, const BaseClass& input1)
{
    m_inputNode.push_back(&input0);
    m_inputNode.push_back(&input1);
}

Therefore the shared_ptr<>s must point to const BaseClass. When you don't want that, you'll have to drop const from the parameter specifications.

I don't recommend an API like that for your needs. You don't make explicit for the caller, that you remember pointers to the objects. He might pass in stack allocated or even temporary objects, that are not valid any more, when you later try to use them:

myElement.setNodes(createElement0(), createElement1());

Prefer to be explicit about what the user of your function has to expect.

void Element::setNodes(std::shared_ptr<BaseClass> input0,
                       std::shared_ptr<BaseClass> input1)
{
    m_inputNode.push_back(input0);
    m_inputNode.push_back(input1);
}

Now the user has to explicitly provide pointers:

myElement.setNodes(std::make_shared<Element>(createElement0()),
                   std::make_shared<Element>(createElement1()));

This tells the user, that you are going to take shared ownership of that object.

Upvotes: 1

Bathsheba
Bathsheba

Reputation: 234775

If you know that someone else is managing the lifetime of the vector elements, then use std::vector<const BaseClass*> as the type, and use &input0 etc. when pushing elements to the vector.

It's difficult to use a std::shared_ptr pointer here since, at the point of use, you don't know how the object has been created.

If you want to retain your references then you can always enclose them in a std::reference_wrapper; set std::vector<std::reference_wrapper<const BaseClass>> as the type. But the ownership issues are still prevalent; you could end up with a vector of dangling references.

Upvotes: 3

Konrad Rudolph
Konrad Rudolph

Reputation: 545825

Since you stated,

I want a pointer/reference to the original.

The only choice is to drop the shared_ptr and use a non-owning pointer:

std::vector<BaseClass const*> m_inputNode;

void Element::setNodes(BaseClass const& input0, BaseClass const& input1) {
    m_inputNode.push_back(&input0);
    m_inputNode.push_back(&input1);
}

(Drop all the consts if you need a non-const pointer.)

shared_ptr fundamentally expresses ownership, which doesn’t seem to be what you’re after.

But if I misunderstood you and you actually want to confer (shared) ownership of the objects to your vector, you need to pass the objects into your function via shared pointers as well:

void Element::setNodes(std::shared_ptr<BaseClass> input0, std::shared_ptr<BaseClass> input1) {
    m_inputNode.push_back(input0);
    m_inputNode.push_back(input1);
}

Upvotes: 3

Related Questions