Ben
Ben

Reputation: 4666

Howto avoid copying when casting a vector<Derived*> to a vector<Base*>

Is there a way to avoid copying large vectors, when a function expects a vector with (pointer to) baseclass objects as input but I only have a vector of (pointers to) derived objects?

class Base {};

class Derived : public Base {};

void doStuff(vector<Base*> &vec)
{
    //do stuff with vec objects
}

int main()
{
    vector<Derived*> fooDerived(1000000);

    vector<Base*> fooBase(fooDerived.begin(), fooDerived.end()); // how to avoid copying here?
    doStuff(fooBase);
}

Upvotes: 3

Views: 314

Answers (6)

Daan
Daan

Reputation: 1879

I found this topic interesting where they mention you can safely cast Derived* to Base* (but not the other side) while this is not possible from vector<Derived*> to vector<Base*> but you kind of should be able to... (Ok, the address of the vector changes, thus needing a copy)
One dirty solution they mention is using reinterpret_cast<vector<Base*> >(vector<Derived*>) but not sure if it's better then using the copy constructor... probably the same thing happens.

Upvotes: 0

Kerrek SB
Kerrek SB

Reputation: 477040

If your definition of doStuff() is absolutely mandatory, then you won't get around the copy. Containers of pointers aren't "covariant" with respect to the pointee's class hierarchy, for a whole host of reasons. (For example, if you could treat vector<Derived*> like a vector<Base*>, you could insert Base-pointers into it which wouldn't behave like Derived-pointers. In any event, the parameter type of a container is fixed and part of the container's type.)

If you do have some leeway with the function, you could restructure the code a bit: You could make it a template parametrized on the container, or a template on an iterator range, and/or you could split the actual workload into a separate function. For example:

void doStuffImpl(Base *);

template <typename Iter>
void doStuff(Iter begin, Iter end)
{
  for (Iter it = begin; it != end; ++it)
  {
    doStuffImpl(*it);  // conversion happens here
  }
}

Upvotes: 1

sehe
sehe

Reputation: 392954

You can't cast the vectors. However, you shouldn't really need to either.

If you really want, you could use Boost Iterator (documentation)

static Derived* ToDerived(Base* b)
{
    return dynamic_cast<Derived*>(b); // return null for incompatible subtypes
}

static void DoSomething(Derived* d)
{ 
          if (!d)
               return; // incompatible type or null entry
          // do work
}


// somewhere:
{
    std::vector<Base*> bases;

    std::for_each(
         boost::make_transform_iterator(bases.begin(), &ToDerived),
         boost::make_transform_iterator(bases.end(), &ToDerived),
         DoSomething);
}

Note: a particularly handy effect of using dynamic_cast<Derived*> is that if the runtime type of the object cannot be casted to Derived* (e.g. because it is actually an OtherDerived*, it will simply return a null pointer.

Upvotes: 1

npclaudiu
npclaudiu

Reputation: 2441

One way is to store only pointers to the base class like this:

vector<Base*> v(100);
v.push_back(new Derived());

doStuff(v);

Upvotes: 0

Oswald
Oswald

Reputation: 31647

If you could use a vector<Derived*> as if it where a vector<Base*>, you could add a pointer to a class OtherDerived : public Base to that vector. This would be dangerous.

Upvotes: 2

omt66
omt66

Reputation: 5019

Since your template for the vector is a pointer, your fooBase local variable is going to be using reference. Maybe the question is not very clear, if you could specify clearly we may try to address the problem!

Upvotes: 0

Related Questions