c ++ std :: возможно ли копирование с приведением типа к производному классу?

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

Есть ли более элегантный способ, чем мой второй подход, или мне придется делать это так?

11
задан moka 18 August 2010 в 15:56
поделиться

4 ответа

Неявное преобразование небезопасно, поэтому необходимо сделать его явным. Стандартный алгоритм для применения какого-либо преобразования к последовательности - 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>());
16
ответ дан 3 December 2019 в 03:51
поделиться

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

#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;
}
2
ответ дан 3 December 2019 в 03:51
поделиться

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

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

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

1
ответ дан 3 December 2019 в 03:51
поделиться

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

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

7
ответ дан 3 December 2019 в 03:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: