0

TL;DR
how can I copy the last element of a vector, given that this element is an instance of a template class which is also derived from a virtual base class?


I want to create a vector which will act as an undo stack. the stack should hold float and string based objects (and possibly other types). It might contain all floats, all strings or a combination of both. When a float or a string is added for the first time, I want to add a copy of it to the stack.

This is what I have so far:

class Base
{
...
virtual bool func1() = 0;
}

a derived class with a template argument

template<typename T>
class derived : public base {...}

and these two

class foo : public derived<float>
class bar : public derived<string>

in main:

std::vector<Base> stack;
if (condition) 
stack.push_back(new foo())
else
stack.push_back(new bar())

if stack.back().func1()==true add another foo or bar. func1 is implemented in foo and bar and returns true if it’s the first foo or bar in the stack

if (stack.back().func1())  
 //do something like
 //stack.push_back(new foo) if stack.back() is a foo
 //stack.push_back(new bar) if stack.back() is a bar

Now, I can’t do stack.push_back(new Base) because it has a virtual function. for the same reason I can’t make a copy of stack.back(). I also can’t do stack.push_back(new drived<T>) because I don’t know that T is. I definitely don’t want to hardcode new foo, new bar, string or float because I want to keep the code generic for future derived classes.

So how can I copy the last element of a vector, or add a new element of the same kind, given that this element is a template class which is derived from a virtual base class?

David912
  • 378
  • 1
  • 2
  • 11
  • 2
    You have to use a `std::vector`, or better `std::vector>` for this to work. Normally this problem is solved by making a virtual `clone` method that each derived class can override. – super Feb 08 '21 at 08:17
  • 2
    The templating and indirection through an intermediate class makes no difference - you solve this in exactly the same way as if `foo` and `bar` inherited directly from `Base`. – molbdnilo Feb 08 '21 at 08:23
  • Thanks @super, this is the kind of solution I was looking for. – David912 Feb 08 '21 at 08:30

1 Answers1

2

Traditionally, it's done by adding clone method to the Base:

struct Base {
    virtual std::unique_ptr<Base> clone() const = 0;
    virtual ~Base() = default;
};

CRTP can be used to automate overriding this method here:

template<class Child> struct Clonable: Base {
    std::unique_ptr<Base> clone() const final {
        auto &asChild = *static_cast<Child const *>(this);
        return std::make_unique<Child>(asChild);
    }
};

struct Foo: Clonable<Foo> {};
bipll
  • 11,747
  • 1
  • 18
  • 32