Search code examples
c++vectorunique-ptrabstract-base-class

How / Is it possible to have a vector<unique_ptr<ABC>>?


Revamping some old code where I was manually handling lifetimes of a vector of raw pointers to concrete instances of an abstract base class (ABC).

So the vector's owner had a virtual dtor which manually went through and deleted the contents of the vector, etc.

Since the vector's owner owns the elements in it, it makes a lot of sense to change this to a vector of unique_ptr's.

Sadly, it seems this is impossible? Because a vector<unique_ptr<type>> must be able to have a static dtor for type but because in this case type is an ABC, that is not available, hence the vector won't compile...

Or am I missing something?

e.g.:

struct ABC
{
    ABC() = default;
    virtual ~ABC() { } // need a vtable entry so that concrete subclasses will be deleted through the base dtor

    virtual std::unique_ptr<ABC> Clone() = 0;
    virtual void Foo() = 0;

    std::vector<std::unique_ptr<ABC>>   elements;
};

EDIT2: here's a complete example that fails:

#include <iostream>
#include <memory>
#include <vector>

struct ABC
{
    virtual ~ABC() { } // need a vtable entry so that concrete subclasses will be deleted through the base dtor

    virtual std::unique_ptr<ABC> Clone() = 0;
    virtual void Foo() = 0;

    std::vector<std::unique_ptr<ABC>>   elements;
};

struct Child : ABC
{
    std::unique_ptr<ABC> Clone() override { return std::make_unique<Child>(*this); }
    void Foo() override { };
};

struct Derived : Child
{
    std::unique_ptr<ABC> Clone() override { return std::make_unique<Derived>(*this); }
};

int main()
{
    std::unique_ptr<ABC> x;
    std::unique_ptr<ABC> c = std::make_unique<Child>();

    std::vector<std::unique_ptr<ABC>>   elements;
    elements.emplace_back(std::make_unique<Derived>());
    return 0;
}

Solution

  • The error occurs when you try to copy an instance of Base or an instance of a type derived from Base. The default copy constructor will attempt to copy Base::elements, which attempts to copy each of it's elements into the new vector. Since these elements are unique_ptr<Base> this copy is not allowed.

    This example reproduces the problem :

    #include <memory>
    #include <vector>
    
    struct Base
    {
        using PBase = std::unique_ptr<Base>;
        using VBase = std::vector<PBase>;
    
        VBase   elements;
    };
    
    int main()
    {
        Base x;
        auto y = x; // This line causes the error
    
        return 0;
    }
    

    You will likely need to implement your own copy constructor and copy assignment operator, or forbid copying by deleting these functions. Using shared_ptr instead might work, if shallow copies will work for your application.