How to use arrays or vectors of pointers polymorphically?

78 Views Asked by At

I have read in many different posts regarding this matter, that arrays are not to be treated polymorphically and that one should use arrays of pointers instead, and the reasoning behind it is clear to me.

However, I cannot find an example of how it is done and I cannot seem to get it working. Consider the following piece of code:

#include <vector>

class Base
{
    public:
        Base();
        virtual ~Base();
};

class Derived : Base
{
    public:
        Derived();
        ~Derived();
};

void foo( std::vector<Base*> )
{
    // do something
}

int main()
{
    std::vector<Derived*> bar;
    foo(bar);

    return 0;
}

Compiling this gives the error message

could not convert 'bar' from 'std::vector<Derived*>' to 'std::vector<Base*>

Am I missing something or is the design even fundamentally flawed? Thanks in advance.

3

There are 3 best solutions below

0
On BEST ANSWER

std::vector<Derived*> and std::vector<Base*> are different types and there are no conversions between them. But it looks like what you need is

std::vector<Base*> bar;
foo(bar);

i.e. let the polymorphism work at the level of the elements of the vector.

However, note that in order to use polymorphism in the usual sense, Derived has to inherit publicly from Base:

class Derived : public Base
                ^^^^^^

or

struct Derived : Base
0
On

Am I missing something or is the design even fundamentally flawed?

An object of std::vector<Derived*> cannot be automatically cast to std::vector<Base*>. They are two completely different types.

I can think of the following options to address the issue:

  1. Change foo to accept a std::vector<Derived*>.

    void foo( std::vector<Derived*> )
    {
        // do something
    }
    
  2. Change foo to a function template.

    template <typename T>
    void foo( std::vector<T*> )
    {
        // do something with the implicit understanding that
        // T is derived from Base.
    }
    
  3. Change the calling code to use std::vector<Base*>.

0
On

To complete juanchopanza's answer, this is how you could use your std::vector<Derived*>:

void itemfoo(Base* item)
{
    // do something
}

template<typename Iter>
void foo(Iter begin, Iter end) {
    // do stuff per element
    while(begin != end)
        itemfoo(*begin++);
}

int main()
{
    std::vector<Derived*> bar;
    foo(bar.begin(), bar.end());

    return 0;
}

if you need to do an operation on the whole range and do it as Base* then you can do this:

void foo(Base** begin, Base** end) {
     // do stuff on range [begin, end)
}

int main()
{
    std::vector<Derived*> bar;
    if(bar.empty())
        foo(nullptr, nullptr); // or however you handle the empty range
    else
        foo(&bar[0], &bar[0] + bar.size());

    return 0;
}