Search code examples
c++pointersvectorunique-ptrinitializer-list

Convert initializer_list of pointers to vector of unique_ptr


I created this class, which represents a set of shapes:

class CompoundShape : public Shape {
    private:
    std::vector<std::unique_ptr<Shape>> shapes;

    public:
    CompoundShape(std::initializer_list<Shape *> shapes) : shapes(shapes) {}

    void draw() {
        for_each(shapes.begin(), shapes.end(), [](auto &shape) { shape->draw(); });
    }
};

The class Shape has another child class, SimpleShape. Ideally, I'd want to initialize a CompoundShape like this, without having to worry about freeing the pointers to the shapes afterwards:

CompoundShape shape = {
    new SimpleShape(...),
    new SimpleShape(...),
    new CompoundShape{...},
    ...
}

The only problem, I think, is in the CompoundShape constructor, where I try to do shapes(shapes) which of course doesn't work, because a std::vector<std::unique_ptr<Shape>> can't be initialized with a std::initializer_list<Shape *> shapes.

What would be the best way of achieving this conversion?


Solution

  • Sad that we cannot use std::initializer_list<std::unique_ptr<Shape>>.

    You can still use constructor of vector taking 2 iterators to do the (not so explicit) conversion:

    CompoundShape(std::initializer_list<Shape *> shapes) : shapes(shapes.begin(), shapes.end()) {}
    

    Demo

    vector constructor would do something similar to

    template <typename T>
    template <typename It> // SFINAE to avoid to conflict with vector(std::size_t, T) when T=size_t
    std::vector<T>::vector<T>(It begin, It end) :
        m_size(std::distance(begin, end)),
        m_capacity(m_size),
        m_data(allocate<T>(m_size))
    {
        std::size_t i = 0;
        for (auto it = begin; it != end; ++it, ++i) {
            new (&data[i]) T(*it); // placement new, calling constructor deferencing iterator
        }
    }
    

    So in your case

    std::initializer_list<Shape *>::iterator it /* = .. */;
    Shape* shape = *it;
    new (&data[i]) std::unique_ptr<Shape>(shape);