Во-первых, я бы сказал, что если вы столкнетесь с этой проблемой, вы должны выяснить, почему в вашем проекте вы должны выполнить этот шаг. Возможно, есть что-то, что вы могли бы сделать по-другому, чтобы избежать этой проблемы. Лично я считаю подозрительным, что вы генерируете контейнер Base*
, каждый из которых указывает только на Derived
объекты.
Но если вы хотите сделать это, есть несколько возможностей, как go об этом. Если B
не является виртуальным базовым классом D
, везде вы можете использовать static_cast
вместо dynamic_cast
из-за [expr.stati c .cast] / 11 (Короче говоря, если вы теперь, когда dynamic_cast
будет работать, вы также можете static_cast
). Это сэкономит вам проверку во время выполнения в dynamic_cast
.
Преобразование с использованием памяти
Вы в основном создаете второй вектор и копируете все указатели:
const auto vb = get_b();
std::vector<const Derived*> ret;
ret.reserve(vb.size());
std::transform(cbegin(vb), cend(vb), std::back_inserter(ret), [](const Base* p) { return dynamic_cast<const Derived*>(p); });
return ret;
Это, на мой взгляд, самый быстрый и краткий способ сделать это только с помощью stl в C ++ 14. Я не очень разбираюсь в возможностях наддува. Если вы можете использовать какой-либо итератор преобразования, вы можете инициализировать ret
непосредственно из двух итераторов.
Нет дополнительной памяти, шаблонный код, небольшие затраты времени выполнения доступа
Оберните ваш std::vector<const Base*>
в собственный класс, который работает как вектор и возвращает const Derived*
при доступе. С dynamic_cast
это приведет к небольшим накладным расходам времени выполнения при доступе, так как он должен будет выполнить проверку. Если вы можете использовать static_cast
(как говорилось выше), это не будет иметь место. (Вы можете иметь очень небольшие накладные расходы из-за дополнительного уровня косвенности).
Мне очень нравится это решение, и я бы лично использовал его. Я не уверен, что в boost есть какой-то контейнерный адаптер, в противном случае вам придется написать немного стандартного кода, чтобы иметь векторный интерфейс (вы могли наследовать от std::vector
и только перезаписывать operator[]
и at()
, но это имеет свои проблемы, поскольку std::vector
не имеет виртуальных методов, включая деструктор!).
Нет памяти и нет накладных расходов времени выполнения
Тебе бы это действительно понравилось, я думаю ^^. Я не думаю, что это возможно с std::vector
. Без лишних затрат времени выполнения вам потребуется вернуть объект, который действительно имеет тип std::vector<const Derived*>
. Без лишних затрат памяти вы должны перезапустить память возвращаемого объекта get_b
.
Но std::vector<T>
не имеет возможности отказаться от владения sh владением своей собственной памятью (за исключением другого std::vector<T>
в том же T
путем замены или перемещения конструкции / назначения). Может быть, вы можете сделать что-то подозрительное с помощью специального распределителя, чтобы базовое хранилище не удалялось при уничтожении вектора, и вы получаете его до уничтожения через data()
. Но это похоже на идеальный способ получить утечку памяти. Тем более, что я действительно не знаю, как это работает с емкостью по сравнению с размером.
Даже если вы получаете базовое хранилище, вы не можете его использовать, поскольку вы не можете построить вектор из уже выделенного фрагмента памяти. Конечно, здесь вы снова можете сделать что-то плохое с помощью пользовательского распределителя, но опять же это кажется плохой идеей.
Можно обойти эту проблему go, используя std::unique_ptr<T[]>
вместо std::vector
. По сути, это компромисс между std::array
и std::vector
. Он содержит массив размера времени выполнения, но размер этого массива является постоянным после его выделения. Здесь вы можете получить хранилище с помощью release()
и создать из него новое std::unique_ptr
без проблем.
Это приводит нас к худшей проблеме. Следующий код недействителен. Вы не можете даже разыграть от Derived**
до Base**
(и давайте даже не будем говорить об обратном)
std::unique_ptr<const Base*[]> base(static_cast<const Base**>(new Derived*[5]));
std::unique_ptr<const Derived*[]> dev(dynamic_cast<const Derived**>(base.release()));
Я понятия не имею, существует ли какой-то странный способ переосмысления всего целого кусок памяти как указатели другого типа. Даже тогда, если это каким-то образом разумно сделать. Поэтому я не вижу способа сделать этот вариант.