c ++ std :: copy с приведением типа к производному классу возможно? - PullRequest
11 голосов
/ 18 августа 2010

Я почти уверен, что нет никакого способа сделать это явно, но я все же хотел бы спросить, если есть лучший способ. У меня есть базовый класс 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 *, я не могу сделать это так, потому что мне пришлось бы явно typecast, например, так:

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);
}

Есть ли что-нибудь более изящное, чем мой второй подход, или мне придется так делать?

Ответы [ 4 ]

16 голосов
/ 18 августа 2010

Не безопасно делать преобразование неявным образом, поэтому вы должны сделать его явным. Стандартный алгоритм применения какого-либо преобразования к последовательности - 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>());
7 голосов
/ 18 августа 2010

Определите функтор для выполнения каста, например.

struct Downcast
{
    B* operator() ( A* a ) const
    {
        return static_cast< B* >( a );
    }
};

и затем используйте std::transform вместо std::copy, т.е.

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() ), чтобы обеспечить одно выделение.

2 голосов
/ 18 августа 2010

Используйте преобразование:

#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;
}
1 голос
/ 18 августа 2010

Вы можете использовать подход адаптера итератора, но я бы посоветовал сделать это правильно, если вы это сделаете. Либо вам нужно переопределить все, что делает итератор «Iterator», либо использовать Boost.Iterator, библиотеку, предназначенную для облегчения таких вещей.

Другой подход, который вы бы использовали - создать функтор и использовать std :: transform вместо std :: copy. Это казалось бы мне намного более легким подходом. Если вы используете компилятор C ++ 0x, вы можете даже использовать лямбду.

Редактировать: Человек, который предложил использовать адаптер, вытащил свой ответ, поэтому первый абзац может не иметь большого смысла. Он использовал обертку вокруг векторных итераторов, которые возвращали B * вместо A *, но он исключил много работы, которая потребовалась бы для правильного выполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...