0

I have a Base class and a Sub class:

class Base{
  public:
    Base(){}
    // ...
};

class Sub: public Base{
    int param;
  public:
    Sub(){}
    // ...
};

And also I have a function that requires a vector of Base like this:

void doSomeThing(vector<Base> vectorOfBases){
    // ...
}

I need to call the function like this:

vector<Sub> myVectorOfSubs;
doSomeThing(myVectorOfSubs);

The compiler tell me that:

there is no appropriate conversion from vector< Sub > to vector< Base >

So how can I pass a vector of Sub class to a function that requires a vector of Base class?

  • I tried this cast doSomeThing( (vector&)myVectorOfSubs); the compiler accepts this but generates an exception while running. – Ghouila Nabil Mar 02 '18 at 21:55
  • `std::vector`s containing related classes are not themselves related. There is no special relation between `std::vector` and `std::vector`. Note that if you try to put the elements of a `std::vector` into a `std::vector` you will experience [object slicing](https://stackoverflow.com/questions/274626/what-is-object-slicing). – François Andrieux Mar 02 '18 at 21:55
  • 1
    You can't. `vector` and `vector` are totally different types. The normal way is to pass a pair of iterators. A pointer and a length would do in a pinch too. – nwp Mar 02 '18 at 21:55
  • Yes François Andrieux I know that, but I don't care about _Sub.param_ – Ghouila Nabil Mar 02 '18 at 21:59
  • nwp can you explain how to pass the pair of iterators? – Ghouila Nabil Mar 02 '18 at 22:03

2 Answers2

5

vector<Base> and vector<Sub> hold actual Base and Sub objects, respectively. They are different vector types, and their data arrays are different, so you can't just pass a vector<Sub> where a vector<Base> is expected. And if try to construct a vector<Base> using Sub objects, slicing will occur.

To make the code work, you can either:

  • change the function to take a vector<Base*> instead, and then you can construct a vector<Base*> whose elements are pointing at your vector<Sub> elements, eg:

    void doSomeThing(std::vector<Base*> &vectorOfBasePtrs) {
        for (Base *b : vectorOfBasePtrs) {
            // ...
        }
    }
    

    std::vector<Sub> myVectorOfSubs;
    ...
    
    std::vector<Base*> myVectorOfBasePtrs;
    
    myVectorOfBasePtrs.resize(myVectorOfSubs.size());
    std::transform(myVectorOfSubs.begin(), myVectorOfSubs.end(), myVectorOfBasePtrs.begin(),
        [](Sub &s){ return static_cast<Base*>(&s); }
    );
    
    /* or:
    myVectorOfBasePtrs.reserve(myVectorOfSubs.size());
    for (Sub &s : myVectorOfSubs) {
        myVectorOfBasePtrs.push_back(&s);
    }
    */
    
    doSomeThing(myVectorOfBasePtrs);
    
  • change the function to take a vector<T> where T is a template parameter (and ideally use SFINAE via std::enable_if and std::is_base_of to make sure that T is actually Base or a type derived from Base):

    template<typename T>
    void doSomeThing(std::vector<T> &vectorOfObjs) {
        for (Base &b : vectorOfObjs) {
            // ...
        }
    }
    

    std::vector<Sub> myVectorOfSubs;
    ...
    
    doSomeThing(myVectorOfSubs);
    
  • change the function to take a templated T for the actual container instead of the container element type:

    template<typename Container>
    void doSomeThing(Container &containerOfObjs) {
        for (Base &b : containerOfObjs) {
            // ...
        }
    }
    
  • change the function to take a pair of iterators instead of a container itself, and then you can pass in iterators from your vector<Sub>:

    template <typename Iterator>
    void doSomeThing(Iterator begin, Iterator end) {
        while (begin != end) {
            Base &b = *begin;
            // ...
            ++begin;
        }
    }
    

    std::vector<Sub> myVectorOfSubs;
    ...
    
    doSomeThing(myVectorOfSubs.begin(), myVectorOfSubs.end());
    
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

So how can I pass a vector of Sub class to a function that requires a vector of Base class?

You cannot.

There is no superclass - sublcass relationship between std::vector<Base> and std::vector<Sub>.

Here's one way to get around that limitation. Change the function to use a function template that can work with any container that contains Base or Sub objects.

template <typename Container>
void doSomeThing(Container& container)
{
   // Do something for each item of the container assuming that
   // the container contains objects of type Base or any of its derived classes.
   for ( Base& baseRef : container )
   {
     // Use baseRef
   }
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270