Reputation: 4491
I am pretty sure that there is no way around doing this explicitly but I would like to ask nontheless in case there is a better way. I have a base class A and a derived class B, now I have a std::list of A* which point to B*'s and I want to copy this list of A*'s to a std::vector of B*'s so basically I want to do this:
std::list<A*> aList = someObject.getAs();
std::vector<B*> bVec = std::vector<B*>(aList.begin(), aList.end());
I am pretty sure this should compile when the list and the vector would be the same type (eg both were A*'s), but since in this case A* is the base class of B* I can't do it this way, because I would have to explicitly typecast for instance like this:
std::list<A*> aList = someObject.getAs();
std::vector<B*> bVec;
bVec.reserve(aList.size());
std::list<A*>::iterator it = aList.begin();
for(it; it!=aList.end(); ++it)
{
B* b = static_cast<B*>(*it);
bVec.push_back(b);
}
Is there any more elegant way than my second approach or will I have to do it like that?
Upvotes: 13
Views: 7545
Reputation: 40859
You could go the iterator adapter approach, but I'd suggest doing it correctly if you do. Either you need to override all the stuff that makes an iterator an "Iterator", or use Boost.Iterator, a library meant to make such things easier.
The other approach you would use is make a functor and use std::transform instead of std::copy. This would seem to me a much easier approach. If you're using a C++0x compiler you could even just use a lambda.
Edit: The person who suggested using an adapter pulled his answer so the first paragraph might not make a lot of sense. It used a wrapper around the vector iterators that returned B* instead of A* but it left out a whole lot of work that would be necessary to do it correctly.
Upvotes: 1
Reputation: 254461
It's not safe do the conversion implicitly, so you have to make it explicit. The standard algorithm for applying some kind of transformation to a sequence is std::transform
, which you can use to populate an empty container as follows:
struct A {};
struct B : A {};
template <typename From, typename To>
struct static_caster
{
To* operator()(From* p) {return static_cast<To*>(p);}
};
std::list<A*> a;
std::vector<B*> b;
std::transform(a.begin(), a.end(), std::back_inserter(b), static_caster<A,B>());
Upvotes: 20
Reputation: 13421
Define a functor to do the casting eg.
struct Downcast
{
B* operator() ( A* a ) const
{
return static_cast< B* >( a );
}
};
and then use std::transform
instead of std::copy
i.e.
bVec.resize(aList.size());
std::transform( aList.begin(), aList.end(), bVec.begin(), Downcast() );
Note you can also do
std::vector<B*> bVec;
std::transform( aList.begin(), aList.end(), std::back_inserter( bVec ), Downcast() );
in which case bVec
will grow as needed but I prefer the first approach to be absolutely sure the memory allocation is all done at once. As @Mike Seymour points out though you could call bVec.reserve( aList.size() )
in the second case to ensure one allocation.
Upvotes: 7
Reputation: 101456
Use a transformation:
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;
class A
{
};
class B : public A
{
};
A* get_a() { return new B; }
B* make_b(A* a) { return static_cast<B*>(a); }
int main()
{
vector<A*> a_list;
vector<B*> b_list;
generate_n(back_inserter(a_list), 10, get_a);
transform(a_list.begin(), a_list.end(), back_inserter(b_list), make_b);
return 0;
}
Upvotes: 2