2

I have this code:

#include <iostream>
#include <memory>

class Base
{
public:
    virtual void doSmth() = 0;
};

class Der1 : Base
{
public:
    void doSmth() final
    {
        std::cout << "Hello from der1\n";
    }
};

class Der2 : Base
{
public:
    void doSmth() final
    {
        std::cout << "Hello from der2\n";
    }
};

class Owner
{
public:
    Owner(std::unique_ptr<Base> passedptr)
    {
        ptr = std::move(passedptr);
    }

    std::unique_ptr<Base> ptr;
};

int main()
{
    auto der = std::make_unique<Der1>();
    Owner owner(std::move(der)); // Fails, need to convert
    owner.ptr->doSmth();
}

I want the Owner class to take a base pointer in constructor and set its unique_ptr member to it. I understand that I need to pass it with std::move(), but I don't understand how I can convert it to the base class.

I've seen unique_ptr to a derived class as an argument to a function that takes a unique_ptr to a base class, but solutions from it don't seem to work, I can't just move a pointer to a derived class like I do it in my code, and I can't create a base pointer like this:

std::unique_ptr<Base> der(new Der1());

How can I convert and pass ownership properly?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
SavedowW
  • 39
  • 5
  • 6
    You need to inherit publicly if you want the derived class to be useable as a base class. `class Der1 : Base` -> `class Der1 : public Base` – NathanOliver Jul 07 '23 at 18:53
  • 1
    In the future, show the complete compile error message, including informational lines that may accompany the error in the compiler output but not get parsed to the "Errors List" window in your IDE. – Ben Voigt Jul 07 '23 at 18:55

1 Answers1

5

How can I convert and pass ownership properly?

There are a few steps you need to do, to make your code correct.

  1. The derived classes should be inheriting the Base, publically, so that all the compiler generated constructors of Base (i.e. move constructor and move assignment) also be useful for them.

  2. Base class is missing a virtual destructor, and you are assigning the child pointers (i.e. in Owner class), which will be therefore deleted through Base class pointers (i.e. std::unique_ptr<Base> ptr;). This will lead to undefined behavior.

    Read here more: When to use virtual destructors?

  3. In Owner, you should be better using constructor initializer list, instead of constructing the Owner and assigning the members separately.

  4. (Optionally), you might probably want to provide a template constructor for Owner, so that one can pass both Der1 and Der2.

The code would look like: See live demo in godbolt.org


class Base
{
public:
    virtual void doSmth() = 0;
    virtual ~Base() = default; // add this for designed behavior!
};

class Der1 : public Base
//           ^~~~~~~ ---> Inherit public ally
{
public:
    // ....
};

class Der2 : public Base
//           ^~~~~~~ ---> Inherit public ally
{
public:
    // ....
};

class Owner
{
public:
    template<typename Derived> // --> Templated
    Owner(std::unique_ptr<Derived>&& passedptr)
        : ptr(std::move(passedptr))
    {}

    std::unique_ptr<Base> ptr;
};
JeJo
  • 30,635
  • 6
  • 49
  • 88