Kill KRT
Kill KRT

Reputation: 1261

Upcast unique_ptr with template

Consider the following simple code:

#include <memory>

template<typename T>
struct Foo
{
    virtual T & foo() = 0;
};

struct Bar : public Foo<int>
{
    int x;
    virtual int & foo() override
    {
        return x;
    }
};

void baz(std::unique_ptr<Foo<int>>)
{
}

int main()
{
    auto up = std::make_unique<Bar>();
    baz(up);
    return 0;
}

It won't compile reporting this error:

prog.cc:27:9: error: could not convert 'up' from 'unique_ptr<Bar,default_delete<Bar>>' to 'unique_ptr<Foo<int>,default_delete<Foo<int>>>'
   27 |     baz(up);
      |         ^~
      |         |
      |         unique_ptr<Bar,default_delete<Bar>>

I wonder how I can solve this problem. share_ptr seems to have proper casting methods to solve this issue, while it seems that for unique_ptr there no alternatives.

Upvotes: 0

Views: 507

Answers (1)

Fureeish
Fureeish

Reputation: 13424

First of all, you are taking the argument in baz by value, which would imply copying a std::unique_ptr and... that's impossible (or std::moveing it, but I doubt that's what you want here).

Second of all, std::unique_ptr doesn't convert because that would require copying.

What's the workaround?

You can do:

std::unique_ptr<Foo<int>> up = std::make_unique<Bar>();

instead of:

auto up = std::make_unique<Bar>();

and take the argument in baz by reference:

void baz(std::unique_ptr<Foo<int>>&) { ... }

First thing is to save a unique_ptr to derived in a unique_ptr to base. auto cannot do that, unless you use a cast.

Second thing is, as aforementioned, to not copy the unique_ptr.

What's more, you code exhibits undefined behaviour, because you delete the derived class object with a base class pointer. That requires a virtual destructor in the base class.

Upvotes: 2

Related Questions