cypheratheist
cypheratheist

Reputation: 173

Casting shared_ptr in initializer list

Is it possible to up-cast a shared pointer object reference in an initializer list?

I have two class hierarchies with matching derived classes. Let's say I have a Base class which is derived to a Foo and a Bar class. Then I have a separate hierarchy with a ProxyBase class and then (to match the other hierarchy) there's FooProxy and BarProxy.

The ProxyBase class stores a shared_ptr<Base>. When a ProxyFoo is created it requires a std::shared_ptr<Foo>&, when a ProxyBar is created it requires a std::shared_ptr<Bar>&.

There are two ways I can make this work:

But I'm wondering if this can be done with references.

I think I know what the problem is: When I remove the references it works, and I think that's because "casting" a shared_ptr<X> is technically not allowed; it can only be "casted" by creating a new shared_ptr object of a derived/base type. When using references one tries to actually cast one shared_ptr object to another which isn't allowed. Is this correct?

Originally I thought this was one of those cases where std::static_pointer_cast would help, but if my hypothesis is correct then that won't help either.

Self-contained example:

#include <memory>

class Base { };

class Foo : public Base
{
private:
  int val_;

public:
  Foo(int val) : val_(val) { }
};

class ProxyBase
{
protected:
  std::shared_ptr<Base> obj_;

  ProxyBase(std::shared_ptr<Base>& obj) : obj_(obj) { }
};

class FooProxy : public ProxyBase
{
public:
  FooProxy(std::shared_ptr<Foo>& fobj) : ProxyBase(fobj) { }
};

int main()
{
  auto f = std::make_shared<Foo>(42);
  auto fp = std::make_shared<FooProxy>(f);
}

So my question boils down to: Is it possible to do what I'm trying to do in the (currently non-functional) code above without removing the references and yet keep the Foo and Bar specific constructors to FooProxy and BarProxy?

Upvotes: 1

Views: 227

Answers (1)

Barry
Barry

Reputation: 303457

A shared_ptr<Derived> can be converted to a shared_ptr<Base>, but it is not a shared_ptr<Base>.

In other words, this compiles:

std::shared_ptr<Derived> pd;
std::shared_ptr<Base> pb = pd;

But this does not:

std::shared_ptr<Base>& pb = pd;

But this does, because const references can bind to temporaries and this will implicitly perform a conversion:

std::shared_ptr<Base> const& pb = pd;

The problem is that you're taking non-const lvalue references in your constructors. You should either take const lvalue references or just take them by value.

Upvotes: 2

Related Questions