Подтвердить что ты не робот

Получение вектора <Производный *> в функцию, которая ожидает вектор <Base *>

Рассмотрим эти классы.

class Base
{
   ...
};

class Derived : public Base
{
   ...
};

эта функция

void BaseFoo( std::vector<Base*>vec )
{
    ...
}

И, наконец, мой вектор

std::vector<Derived*>derived;

Я хочу передать derived в функцию BaseFoo, но компилятор мне не позволяет. Как это решить, не копируя весь вектор в std::vector<Base*>?

4b9b3361

Ответ 1

vector<Base*> и vector<Derived*> являются несвязанными типами, поэтому вы не можете этого сделать. Это объясняется в С++ FAQ здесь.

Вы можете изменить свою переменную от vector<Derived*> до vector<Base*> и вставить в нее Derived объекты.

Кроме того, вы должны передать вектор по const-reference, а не по значению:

void BaseFoo( const std::vector<Base*>& vec )
{
    ...
}

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

#include <boost/ptr_container/ptr_vector.hpp>
boost::ptr_vector<Base> vec;

Альтернативно, измените вектор, чтобы удерживать умный указатель вместо использования необработанных указателей:

#include <memory>
std::vector< std::shared_ptr<Base*> > vec;

или

#include <boost/shared_ptr.hpp>
std::vector< boost::shared_ptr<Base*> > vec;

В каждом случае вам необходимо соответствующим образом изменить вашу функцию BaseFoo.

Ответ 2

Вместо передачи объекта контейнера (vector<>) перейдите в begin и end итераторы, как и остальные алгоритмы STL. Функция, которая их получает, будет шаблонизирована, и не имеет значения, переходите ли вы в Derived * или Base *.

Ответ 3

Эта проблема возникает в языках программирования, которые имеют изменяемые контейнеры. Вы не можете пропустить изменчивый пакет яблок в виде мешка с фруктами, потому что вы не можете быть уверены, что кто-то еще не положит лимон в этот мешок с фруктами, после чего он больше не будет считаться мешком с яблоками. Если бы мешок с яблоками не изменялся, прохождение вокруг него было бы мешком с фруктами. Поиск ковариации/контравариантности.

Ответ 4

одним из вариантов является использование шаблона

template<typename T>
void BaseFoo( const std::vector<T*>& vec)
{
 ...
}

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

Изменить, я должен отметить причину, по которой нам нужен шаблон, потому что мы пытаемся написать тот же код для несвязанных типов, что и некоторые другие плакаты. Шаблоны позволяют решить эти проблемы. Я также обновил его для использования ссылки const. Вы также должны передавать "тяжелые" объекты, такие как вектор по ссылке const, когда вам не нужна копия, которая в основном всегда.

Ответ 5

Как правило, вы начинаете с контейнера базовых указателей, а не наоборот.

Ответ 6

Принимая ответ Matt Price, учитывая, что вы заранее знаете, какие типы вы хотите использовать с вашей функцией, вы можете объявить шаблон функции в файле заголовка, а затем добавить явные экземпляры для этих типов:

// BaseFoo.h
template<typename T>
void BaseFoo( const std::vector<T*>& vec);

// BaseFoo.cpp
template<typename T>
void BaseFoo( const std::vector<T*>& vec);
{
 ...
}

// Explicit instantiation means no need for definition in the header file.
template void BaseFoo<Base> ( const std::vector<Base*>& vec );
template void BaseFoo<Derived> ( const std::vector<Derived*>& vec );

Ответ 7

Если std::vector поддерживает то, о чем вы просите, тогда можно было бы победить систему типов С++ без использования каких-либо трансляций (отредактируйте: ссылка ChrisN на С++ FAQ Lite расскажет об одной и той же проблеме):

class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};

void pushStuff(std::vector<Base*>& vec) {
    vec.push_back(new Derived2);
    vec.push_back(new Base);
}

...
std::vector<Derived1*> vec;
pushStuff(vec); // Not legal
// Now vec contains a Derived2 and a Base!

Так как ваша функция BaseFoo() принимает вектор по значению, он не может изменить исходный вектор, который вы передали, поэтому то, что я написал, было бы невозможно. Но если он принимает неконстантную ссылку и вы используете reinterpret_cast<std::vector<Base*>&>() для передачи вашего std::vector<Derived*>, вы можете не получить результат, который вы хотите, и ваша программа может потерпеть крах.

Java массивы поддерживают ковариационный подтипирование, и для этого требуется Java выполнять проверку типа выполнения каждый раз, когда вы храните значение в массиве. Это тоже нежелательно.

Ответ 8

Они не связаны между собой - вы не можете.

Ответ 9

Если вы имеете дело с сторонней библиотекой, и это ваша единственная надежда, вы можете сделать это:

BaseFoo (*reinterpret_cast<std::vector<Base *> *>(&derived));

В противном случае исправьте свой код одним из других предложений.