views:

1191

answers:

9

Hi,

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*>?

+4  A: 

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.

Matt Price
Potentially the compiler is allowed to optimize the two instances of the function into just one by realizing the object code generated is exactly the same. Doubt many would do so with a complicated function though.
Greg Rogers
Interesting, I've seen the linker throw away redundant code.
Matt Price
You can avoid the need to define the function in the header file by using explicit instantiations. You might merge my example (below) into your answer.
Richard Corden
A: 

They are unrelated types -- you can't.

Lou Franco
+1  A: 

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

Greg Rogers
I think both cases do happen in practice. But I would agree a vector<Base*> is more common in good OO design.
Matt Price
+18  A: 

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

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

Also, you should pass the vector 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::tr1::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.

ChrisN
+1: Complete and precise answer.
ereOn
A: 

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.

eduffy
While that might actually work, it feels like a ticking time bomb of code. I'm very hesitant to use reinterpret_cast unless I know exactly what I'm doing.
Matt Price
+8  A: 

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*.

Frank Krueger
A: 

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.

bk1e
A: 

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 );
Richard Corden
+4  A: 

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.