Получение вектора <Производный *> в функцию, которая ожидает вектор <Base *>
Рассмотрим эти классы.
class Base
{
...
};
class Derived : public Base
{
...
};
эта функция
void BaseFoo( std::vector<Base*>vec )
{
...
}
И, наконец, мой вектор
std::vector<Derived*>derived;
Я хочу передать derived
в функцию BaseFoo
, но компилятор мне не позволяет. Как это решить, не копируя весь вектор в std::vector<Base*>
?
Ответы
Ответ 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));
В противном случае исправьте свой код одним из других предложений.