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

StackOverflow https://stackoverflow.com/questions/114819

  •  02-07-2019
  •  | 
  •  

Вопрос

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

class Base
{
   ...
};

class Derived : public Base
{
   ...
};

эта функция

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

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

std::vector<Derived*>derived;

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

Это было полезно?

Решение

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

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

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

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 функционировать соответственно.

Другие советы

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

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

один из вариантов — использовать шаблон

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

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

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

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

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

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

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

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

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

Если std::vector поддержал то, о чем вы просите, тогда можно было бы победить систему типов C++ без использования каких-либо приведения (редактируйте:Ссылка ChrisN на FAQ по C++ 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 выполнять проверку типа во время выполнения каждый раз, когда вы сохраняете значение в массиве.Это тоже нежелательно.

Это несвязанные типы — вы не можете.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top