Question

Consider these classes.

class Base
{
   ...
};

class Derived : public Base
{
   ...
};

this function

void BaseFoo( std::vector<Base*>vec )
{
    ...
}

And finally my vector

std::vector<Derived*>derived;

I want to pass derived to function BaseFoo, but the compiler doesn't let me. How do I solve this, without copying the whole vector to a std::vector<Base*>?

Was it helpful?

Solution

vector<Base*> and vector<Derived*> are unrelated types, so you can't do this. This is explained in the C++ FAQ here.

You need to change your variable from a vector<Derived*> to a vector<Base*> and insert Derived objects into it.

Also, to avoid copying the vector unnecessarily, you should pass it by const-reference, not by value:

void BaseFoo( const std::vector<Base*>& vec )
{
    ...
}

Finally, to avoid memory leaks, and make your code exception-safe, consider using a container designed to handle heap-allocated objects, e.g:

#include <boost/ptr_container/ptr_vector.hpp>
boost::ptr_vector<Base> vec;

Alternatively, change the vector to hold a smart pointer instead of using raw pointers:

#include <memory>
std::vector< std::shared_ptr<Base*> > vec;

or

#include <boost/shared_ptr.hpp>
std::vector< boost::shared_ptr<Base*> > vec;

In each case, you would need to modify your BaseFoo function accordingly.

OTHER TIPS

Instead of passing the container object (vector<>), pass in begin and end iterators like the rest of the STL algorithms. The function that receives them will be templated, and it won't matter if you pass in Derived* or Base*.

This problem occurs in programming languages that have mutable containers. You cannot pass around a mutable bag of apples as a bag of fruit because you cannot be sure that someone else does not put a lemon into that bag of fruit, after which it no longer qualifies as a bag of apples. If the bag of apples were not mutable, passing it around as a bag of fruit would be fine. Search for covariance/contravariance.

one option is to use a template

template<typename T>
void BaseFoo( const std::vector<T*>& vec)
{
 ...
}

The drawback is that the implementation has to be in the header and you will get a little code bloat. You will wind up with different functions being instantiated for each type, but the code stays the same. Depending on the use case it's a quick and dirty solution.

Edit, I should note the reason we need a template here is because we are trying to write the same code for unrelated types as noted by several other posters. Templates allow you do solve these exact problems. I also updated it to use a const reference. You should also pass "heavy" objects like a vector by const reference when you don't need a copy, which is basically always.

Generally you would start with a container of base pointers, not the other way.

Taking Matt Price's answer from above, given that you know in advance what types you want to use with your function, you can declare the function template in the header file, and then add explicit instantiations for those types:

// BaseFoo.h
template<typename T>
void BaseFoo( const std::vector<T*>& vec);

// BaseFoo.cpp
template<typename T>
void BaseFoo( const std::vector<T*>& vec);
{
 ...
}

// Explicit instantiation means no need for definition in the header file.
template void BaseFoo<Base> ( const std::vector<Base*>& vec );
template void BaseFoo<Derived> ( const std::vector<Derived*>& vec );

If you dealing with a third-party library, and this is your only hope, then you can do this:

BaseFoo (*reinterpret_cast<std::vector<Base *> *>(&derived));

Otherwise fix your code with one of the other suggesstions.

If std::vector supported what you're asking for, then it would be possible to defeat the C++ type system without using any casts (edit: ChrisN's link to the C++ FAQ Lite talks about the same issue):

class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};

void pushStuff(std::vector<Base*>& vec) {
    vec.push_back(new Derived2);
    vec.push_back(new Base);
}

...
std::vector<Derived1*> vec;
pushStuff(vec); // Not legal
// Now vec contains a Derived2 and a Base!

Since your BaseFoo() function takes the vector by value, it cannot modify the original vector that you passed in, so what I wrote would not be possible. But if it takes a non-const reference and you use reinterpret_cast<std::vector<Base*>&>() to pass your std::vector<Derived*>, you might not get the result that you want, and your program might crash.

Java arrays support covariant subtyping, and this requires Java to do a runtime type check every time you store a value in an array. This too is undesirable.

They are unrelated types -- you can't.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top