I have Base and Derived classes that I need polymorphic behaviour from, via the virtual member function foo():
#include <iostream>
#include <memory>
class Base {
public:
virtual int foo() const = 0;
};
class Derived : public Base {
public:
int foo() const override { return 42; }
};
std::unique_ptr<Base> clone(Base & base) {
//auto copy = base; // cannot create an abstract type
//return std::make_unique(copy);
return std::make_unique(base);
}
int main() {
auto d = Derived();
auto p = clone(d);
std::cout << p->foo() << '\n';
}
Does not compile: https://godbolt.org/z/voaGdf1sM
<source>: In function 'std::unique_ptr<Base> clone(Base&)':
<source>:19:28: error: no matching function for call to 'make_unique(Base&)'
19 | return std::make_unique(base);
| ~~~~~~~~~~~~~~~~^~~~~~
Outside of clone(), I want to be able to treat instances of Derived with value semantics throughout most of the program, but there is a case where I need to add instances of Derived to a collection (std::vector<std::unique_ptr<Base>>) and retain polymorphism, so in this case I need to be able to add unique_ptrs to new copies of the Derived objects. Thus I have the function clone() which takes a reference to a Derived object and is intended to make a copy that is then owned by a unique_ptr, and this is returned.
Unfortunately I cannot work out how to make a polymorphic copy of a reference to an abstract type like Base.
I cannot pass the parameter by value, and then move the implicit copy into the unique_ptr, as it's not possible to have an instance of Base, and I need the polymorphism.
I looked into using a forwarding reference, something like:
std::unique_ptr<Base> clone(Base && base) {
return std::make_unique(base);
}
// ...
auto p = clone(std::move(d));
But I don't want to expose the ownership at the caller and require them to call std::move - it should be free to pass in a reference to an existing object and expect clone to copy it, not take ownership of it. I actually want it to be copied, and for the source object to remain intact and able to be used again (e.g. to make more copies).
Note: if I could get this working, it would probably be better for clone to take a const Base &, as I'm passing by reference for performance reasons after all.
Is what I'm trying to do here - make a copy, that is managed as a unique_ptr, without imposing move semantics on the caller - even possible?
I think you're looking for something like this:
#include <iostream>
#include <memory>
class Base {
public:
virtual ~Base () { }
virtual int foo() const = 0;
virtual std::unique_ptr <Base> clone (void) const = 0;
};
class Derived : public Base {
public:
int foo() const override { return 42; }
std::unique_ptr <Base> clone (void) const override
{
auto result = std::make_unique <Derived> ();
*result = *this;
return result;
}
};
int main() {
auto d = Derived ();
auto p = d.clone ();
std::cout << p->foo() << '\n';
}
I'm not sure there's much else to say really, although you can also do:
Base &base = d;
...
auto p = b.clone ();
which I think gets closer to what you're asking for.
Live demo
Edit: added a virtual destructor to Base as per @eerorika's comment, how sloppy of me to omit it!
The unfortunate potential repetition in Paul's solution can be alleviated using the Curiously recurring template pattern:
template <class T>
class TBase : public Base { // Base from Paul's answer
public:
std::unique_ptr<Base> clone() const override {
const T& ref = static_cast<const T&>(*this);
return std::make_unique<T>(ref);
}
};
class Derived1 : public TBase<Derived1> {
public:
int foo() const override { return 42; }
};
class Derived2 : public TBase<Derived2> {
public:
int foo() const override { return 1337; }
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With