Я совершенно уверен, что нет никакого способа сделать это явно, но я все же хотел бы спросить, если есть лучший способ. У меня есть базовый класс A и производный класс B, теперь у меня есть std :: list из A *, которые указывают на B *, и я хочу скопировать этот список A * в std :: vector из B * так что в основном я хочу сделать это:
std::list<A*> aList = someObject.getAs();
std::vector<B*> bVec = std::vector<B*>(aList.begin(), aList.end());
Я почти уверен, что это должно скомпилироваться, когда список и вектор будут одного типа (например, оба были A *), но так как в этом случае A * базовый класс B * я не могу сделать это таким образом, потому что мне пришлось бы явно типизировать пример, например, так:
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);
}
Есть ли более элегантный способ, чем мой второй подход, или мне придется делать это так?
Неявное преобразование небезопасно, поэтому необходимо сделать его явным. Стандартный алгоритм для применения какого-либо преобразования к последовательности - std :: transform
, который можно использовать для заполнения пустого контейнера следующим образом:
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>());
Используйте преобразование:
#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;
}
Вы можете использовать подход адаптера итератора, но я бы посоветовал сделать это правильно, если вы это сделаете. Либо вам нужно переопределить все, что делает итератор «итератором», либо использовать Boost.Iterator, библиотеку, призванную упростить такие вещи.
Другой подход, который вы могли бы использовать, - это создать функтор и использовать std :: transform вместо std :: copy. Мне это показалось бы гораздо более простым подходом. Если вы используете компилятор C ++ 0x, вы можете даже просто использовать лямбда.
Изменить: человек, который предложил использовать адаптер, вытащил свой ответ, поэтому первый абзац может не иметь большого смысла. Он использовал оболочку вокруг векторных итераторов, которые возвращали B * вместо A *, но при этом упускал из виду большую часть работы, которая была бы необходима, чтобы сделать это правильно.
Определите функтор для преобразования, например.
struct Downcast
{
B* operator() ( A* a ) const
{
return static_cast< B* >( a );
}
};
, а затем используйте std :: transform
вместо std :: copy
ie
bVec.resize(aList.size());
std::transform( aList.begin(), aList.end(), bVec.begin(), Downcast() );
Обратите внимание, что вы также можете использовать
std::vector<B*> bVec;
std::transform( aList.begin(), aList.end(), std::back_inserter( bVec ), Downcast() );
, в этом случае bVec
будет расти по мере необходимости, но я предпочитаю первый подход, чтобы быть абсолютно уверенным, что распределение памяти выполняется сразу. Как отмечает @Mike Seymour, вы можете вызвать bVec.reserve (aList.size ())
во втором случае, чтобы гарантировать одно выделение.