Как избежать копирования при приведении вектора <Derived *> к вектору <Base *> - PullRequest
3 голосов
/ 26 сентября 2011

Есть ли способ избежать копирования больших векторов, когда функция ожидает вектор с (указателем на) объектами базового класса в качестве входных данных, но у меня есть только вектор (указателей на) производных объектов?

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

Ответы [ 6 ]

2 голосов
/ 26 сентября 2011

Если бы вы могли использовать vector<Derived*>, как если бы оно было vector<Base*>, вы могли бы добавить указатель на class OtherDerived : public Base на этот вектор.Это было бы опасно.

1 голос
/ 26 сентября 2011

Если ваше определение doStuff() является абсолютно обязательным, вы не сможете обойти копию.Контейнеры указателей не являются «ковариантными» по отношению к иерархии классов пуантеев по целому ряду причин.(Например, если вы можете рассматривать vector<Derived*> как vector<Base*>, вы можете вставить в него Base -поинтеры, которые не будут вести себя как Derived -поинтеры. В любом случае тип параметра контейнераисправлено и является частью типа контейнера.)

Если у вас есть некоторая свобода действий с функцией, вы можете немного реструктурировать код: вы можете сделать его параметризованным шаблоном в контейнере или шаблоном итератора.диапазон, и / или вы можете разделить фактическую рабочую нагрузку на отдельную функцию.Например:

void doStuffImpl(Base *);

template <typename Iter>
void doStuff(Iter begin, Iter end)
{
  for (Iter it = begin; it != end; ++it)
  {
    doStuffImpl(*it);  // conversion happens here
  }
}
1 голос
/ 26 сентября 2011

Вы не можете разыграть векторы. Тем не менее, вам действительно не нужно.

Если вы действительно хотите, вы можете использовать Boost Iterator ( документация )

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

Примечание : особенно удобный эффект использования dynamic_cast<Derived*> заключается в том, что если тип времени выполнения объекта не может быть приведен к Derived* (например, поскольку это на самом деле OtherDerived*, он просто вернет указатель null.

0 голосов
/ 02 сентября 2015

Мне показалось эта тема интересной, где они упоминают, что вы можете безопасно привести Derived* к Base* (но не к другой стороне), пока это невозможно с vector<Derived*> до vector<Base*>, но вывид должен быть в состоянии ... ( Хорошо, адрес вектора изменяется, поэтому требуется копия )
Одно грязное решение, которое они упоминают, использует reinterpret_cast<vector<Base*> >(vector<Derived*>), но не уверен, что лучше, чем тогдаиспользуя конструктор копирования ... вероятно, происходит то же самое.

0 голосов
/ 26 сентября 2011

Один из способов - хранить только указатели на базовый класс следующим образом:

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

doStuff(v);
0 голосов
/ 26 сентября 2011

Поскольку ваш шаблон для вектора является указателем, ваша локальная переменная fooBase будет использовать ссылку.Возможно, вопрос не очень ясен, если бы вы могли четко указать, мы можем попытаться решить проблему!

...