Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What do I Need to Return an Object with a unique_ptr Member?

Let's say that I have this object:

struct foo {
    std::unique_ptr<int> mem;
    virtual ~foo() = default;
};

I can no longer return a foo object created in a function:

foo make_foo() {
    foo result;

    result.mem = std::make_unique<int>({});
    return result;
}

As may be indicated I need the destructor to be virtual because this will be a base class. But even if I'm using the default destructor, that's not enough, I still can't return an object created in a function.

I get the error:

error C2280: foo::foo(const foo &): attempting to reference a deleted function

Is there a way to navigate around this issue?

like image 736
Jonathan Mee Avatar asked Sep 17 '25 16:09

Jonathan Mee


1 Answers

Per [class.copy.ctor]/8

If the definition of a class X does not explicitly declare a move constructor, a non-explicit one will be implicitly declared as defaulted if and only if [...]

  • X does not have a user-declared destructor.

Since

virtual ~foo() = default;

is a user-declared destructor you no longer have a move constructor so it tries to use the copy constructor but can't because that is deleted as you have a non-copyable member.

To get the move constructor back, and to keep the default constructable, you need to add

foo() = default;
foo(foo&&) = default;
foo &operator=(foo &&) = default; // add this if you want to move assign as well

to foo


The reason you have to add foo() = default; when you add foo(foo&&) = default; is that foo(foo&&) = default; is a used-declared constructor and if you have any user-declared constructors then the default constructor is no longer provided.


This is a "hack" but what you could do is move the virtual destructor into another class and then inherit from that. That will give you a virtual destructor in foo without having to declare it and give you the default constructors you want. That would look like

struct make_virtual
{
    virtual ~make_virtual() = default;
};

struct foo : make_virtual {
    std::unique_ptr<int> mem;
};
like image 149
NathanOliver Avatar answered Sep 19 '25 06:09

NathanOliver