Manage abstract classes in std container

Viewed 272

After a lot of research I still don't understand how to deal with an abstract class collection with smart pointers.

Here are the errors I got:

error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Shape; _Dp = std::default_delete<Shape>]'
   base_ptr s = shapes.front();

error: no matching function for call to 'std::unique_ptr<Shape>::unique_ptr(Shape&)'
   shapes.push(base_ptr(b));

By compiling the minimal code to replicate the error (code online avaiable).

    #include <queue>
    #include <memory>

    class Shape {
    public:
        virtual int getPerimeter() =0;
    };

    typedef std::unique_ptr<Shape> base_ptr;

    class Circle : public Shape {
    public:
        virtual int getPerimeter() { return 1; };
    };

    class Square : public Shape {
    public:
        virtual int getPerimeter() { return 0; };
    };

    class ShapeManager {
    public:
        ShapeManager();
        void useShape() {
            if(shapes.empty())
                throw "Work stack is empty.";

            base_ptr s = shapes.front();
            s->getPerimeter();
            shapes.pop();
        }

        void submitShape(Shape &b) {
            shapes.push(base_ptr(b));
        }
    private:
        std::queue<base_ptr> shapes;
    };

    int main(int argc, char **argv) {
        ShapeManager s();
        Circle c;
        s.submitShape(c);
        s.useShape();
        return 1;
    }

It works if I declare the queue as queue<Shape*> but I don't want to deal with pointers -meaning *.

EDIT, this code compiles. Thanks everyone. This article suggested by Guillaume Racicot helps seeing clearer the situation.

#include <queue>
#include <memory>

class Shape {
public:
    virtual int getPerimeter() =0;
};

typedef std::unique_ptr<Shape> base_ptr;

class Circle : public Shape {
public:
    Circle() {};
    virtual int getPerimeter() { return 1; };
};

class Square : public Shape {
public:
    virtual int getPerimeter() { return 0; };
};

class ShapeManager {
public:
    ShapeManager();
    void useShape() {
        if(shapes.empty())
            throw "Work stack is empty.";

        base_ptr s = std::move(shapes.front());
        s->getPerimeter();
        shapes.pop();
    }

    void submitShape(base_ptr b) {
        shapes.push(std::move(b));
    }
private:
    std::queue<base_ptr> shapes;
};

int main(int argc, char **argv) {
    ShapeManager s;
    base_ptr c = std::make_unique<Circle>();
    s.submitShape(std::move(c));
    s.useShape();
    return 1;
}
2 Answers
Related