2

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.

user1876942
  • 1,411
  • 2
  • 20
  • 32

3 Answers3

3

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.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
3

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);
}
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
1

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.

cdonat
  • 2,748
  • 16
  • 24
  • @KonradRudolph 1. I don't see, where i did that except for the part, where I explain why the API might be misleading for its user. 2. You are correct here. I'll edit my comment to use `std::make_shared()` instead. – cdonat Oct 20 '15 at 18:26
  • Thanks for correcting this. Regarding (1), you’re right, your answer explicitly described the caveats. I don’t know why I didn’t see that. – Konrad Rudolph Oct 21 '15 at 08:37