0

I am new to C++ and trying to make list polymorphic/accepting anything deriving from a base class. The issue is that this list must be private, using separate methods to append and interrogate it.

After some research, I was able to get close in a safe manner through smart pointers.

Here is what I have arrived at:

class Shape
{
public:
    Shape(std::string name)
    {
        this->name = name;
    }

    std::string name;
    std::string getName(void)
    {
        return this->name;
    }
};

class ShapeCollector
{
public:
    void addShape(Shape shape)
    {
        this->shapes.push_back(std::make_unique<Shape>("hey"));
    }

private:
    std::vector <std::unique_ptr<Shape>> shapes;
};

I would like to be able to replace the make_unique call with the shape parameter, however nothing I try seems to play correctly.

I could create each derived class inside ShapeCollector, mirroring the constructor arguments as parameters, but this feels very counter intuitive.

Any help would be appreciated!

Lucas
  • 153
  • 1
  • 11
  • 1
    Your code should work. What's the specific problem you're facing with it? Post a [mcve] reproducing your problem as required here please. – πάντα ῥεῖ Apr 28 '19 at 22:01
  • 1
    The make_unique will not work in a polymorphic manner, since the choice of the constructor used by make_unique is done at compile-time. It's not the exact dupe, but the solution proposed in this answer can easily be adapted to solve your problem: https://stackoverflow.com/a/55879235/3723423 – Christophe Apr 28 '19 at 22:11
  • @πάνταῥεῖ the OP explained that he/she can't make it run in a polymorphic manner (see the two sentences after the code snippet). – Christophe Apr 28 '19 at 22:13
  • If you're passing objects of polymorphic type to be added then either you need to pass them as `unique_ptr`s, or add virtual `clone()` method to the base class. As a side note, your `Shape` is not polymorphic, and even if it was, you couldn't pass it by value. – bipll Apr 28 '19 at 22:15
  • Thanks all for the responses. @πάνταῥεῖ this code works, but I would like to be able to add from the parameter ```shape```. @Christophe Thank you, I will take a look at this answer. @bipll Yes this is what I was trying to achieve earlier, but I could not find a matching argument type for this addShape method. Shape is not currently polymorphic, yes. It will be the base class from which other objects will be added to this list. I omitted them to keep the sample code more concise. – Lucas Apr 28 '19 at 22:30

1 Answers1

3

Write addShape to take the derived class as a template parameter:

template<class Derived, class... Args>
void addShape(Args&&... args) {
    // std::forward will correctly choose when to copy or move
    std::unique_ptr<Shape> shape (new Derived(std::forward<Args>(args)...));
    shapes.push_back(std::move(shape)); 
}

This will allow you to give addShape the arguments for the derived classs constructor. For example, if we have aCircle` class:

class Circle : public Shape {
    double radius;
    double x;
    double y;
   public:
    Circle(double radius, double x, double y) 
      : Shape("circle"), radius(radius), x(x), y(y) 
    {}
};

Adding it is simple:

ShapeCollector shapes;
shapes.addShape<Circle>(10.0, 0.0, 0.0); 
Alecto Irene Perez
  • 10,321
  • 23
  • 46