John Bardeen
John Bardeen

Reputation: 105

How can I create a vector of derived objects?

I've read several posts on this website, but I still can't figure out what's the right way to create a std::vector of objects from different derived classes.

I've created a class Column:

template <typename T> class Column {
public:
    Column();
    virtual ~Column();
    vector<T>& getData();
    string getName();
    virtual string getType();

protected:
    string _name;
    vector<T> _data;
};

and many different derived classes like:

class IntColumn : public Column<int> {
public:

    IntColumn();
    IntColumn(string name);
    ~IntColumn();
    vector<int> & getData();
    void addElement(int elem_to_add);
    string getType();

private:
    // ...
};

What I need to do now is to create a vector of Columns which could be IntColumns, FloatColumns, etc... (In my head it would be something like vector<Column> vect; but I can't do it because Column is a template class.)

My final question is: which is the right way to do it?

Upvotes: 1

Views: 1625

Answers (2)

rustyx
rustyx

Reputation: 85286

First of all, you're missing a base class. Column is a template, not a class, and having virtual methods inside a template is rarely useful.

class Column {
public:
    Column();
    virtual ~Column() {}
    std::string getName() const { return _name; }
    virtual std::string getType();

protected:
    std::string _name;
};

template <typename T> class ColumnImpl : public Column {
public:
    ColumnImpl();
    std::vector<T>& getData() { return _data; }
    std::vector<T> const& getData() const { return _data; }

protected:
    std::vector<T> _data;
};

Now you can do

std::vector<std::unique_ptr<Column>> vec;

to store (owning) pointers to Column instances.

Upvotes: 1

eerorika
eerorika

Reputation: 238311

Vectors, like all other standard containers are homogeneous. All elements of the container are of same type. As such, one element cannot be of different type than another. All elements of one vector can be IntColumns or all elements of another vector can be FloatColumns, but you cannot mix and match different element types in one vector.

This problem can be solved with the magic of indirection. There are a few alternatives:

  1. Use a vector of pointer to a common base. This way the vector doesn't contain the objects, but instead the contained pointers point to objects elsewhere. You can bind the lifetime of the pointed object to the pointer by using a smart pointer such as std::unique_ptr.

    However, this only works when different types have a common base. But your classes do not have a common base.

  2. Use a vector of tagged union such as std::variant. This approach does not require inheritance, but is limited to finite set of alternative types.

  3. Use unconstrained type erasure such as vector of void* or std::any. This allows the pointed elements to have any object type, but there is not much that can be done with the objects. I haven't encountered practical use cases for this.

Upvotes: 0

Related Questions