2015-03-03 5 views
3

Каким образом можно разрешить перенос векторам общих указателей на производный класс на функцию, ожидающую вектор общих указателей на базовый класс без выполнения копии?Передача векторов полученных общих указателей?

Вот код:

#include <string> 
#include <vector> 
#include <memory> 

class Base { 
public: 
    std::string Name; 
}; 
using BaseList = std::vector<std::shared_ptr<Base>>; 

class Derived : Base { 
}; 
using DerivedList = std::vector<std::shared_ptr<Derived>>; 

class BaseHandler { 
public: 
    void process(BaseList list) { 
    } 
}; 

int main() { 

    DerivedList list; 

    BaseHandler bh; 
    bh.process(list); 

} 

Код ссылки: http://coliru.stacked-crooked.com/a/5a5b18ba3b2a4f08

EDIT: DOH !!! Я написал не тот. Извините, что ... вот общий_ptr.

+2

Я не вижу общих указателей. –

+2

Нет способа, правильного или иного. –

+0

Копия чего? Из 'std :: shared_ptr'? Это не так много копировать их (их общее состояние не сильно скопировано кстати). – vsoftco

ответ

2

Вы можете попробовать это.

template <class T, 
     class SharedPtr = typename T::value_type, 
     class Element = typename SharedPtr::element_type, 
     class IsDerived = typename std::enable_if<std::is_base_of<Base, Element>::value, void*>::type 
    > 
void process(const T& t) { std::cout << "process" << std::endl; } 

Ключевые идеи:

  1. Вместо того, чтобы доступ к элементам с помощью указателей базового класса, мы можем получить доступ к их через конкретную информацию типа, известный компилятор.
  2. Этот шаблон функции использует трюк под названием «SFINAE», чтобы проверить, является ли этот параметр контейнером интеллектуального указателя производного класса.


Последующие:

Преобразование из «контейнера общего указателя на производный класс» в «контейнер общего указателя на базовый класс» можно и не очень сложно. Однако я отношусь к тому, что выбор дизайна использования «контейнера общего указателя на базовый класс» даст вам приемлемую производительность.


Давайте обсудим, как это сделать в первую очередь.

  1. Мы можем создать std::shared_ptr<Base> объект из каждого std::shared_ptr<Derived> объекта с помощью std::static_pointer_cast.
  2. Чтобы применить std::static_pointer_cast по всем пунктам списка, мы можем использовать std::transform.
  3. Если у вас есть много производных классов, преобразование может быть доступно для всех производных классов, используя шаблон функции с проверкой SFINAE, как указано.

Итак, код выглядит следующим образом:

DerivedList derivedList; 
// Put items into derivedList 

BaseList baseList; 
baseList.reserve(derivedList.size()); 
std::transform(std::begin(derivedList), std::end(derivedList), std::back_inserter(baseList), 
    [](const std::shared_ptr<Derived>& shptr) 
    { 
     return std::static_pointer_cast<Base>(shptr); 
    }); 

BaseHandler bh; 
bh.process(baseList); 

Или:

class BaseForwarder 
{ 
public: 
    template <class T, 
      class SharedPtr = typename T::value_type, 
      class Element = typename SharedPtr::element_type, 
      class IsDerived = typename std::enable_if<std::is_base_of<Base, Element>::value, void*>::type 
     > 
    void process(const T& derivedList) 
    { 
     BaseList baseList; 
     baseList.reserve(derivedList.size()); 

     std::transform(std::begin(derivedList), std::end(derivedList), std::back_inserter(baseList), 
       [](const SharedPtr& shptr) 
       { 
        return std::static_pointer_cast<Base>(shptr); 
       }); 

     BaseHandler bh; 
     bh.process(baseList); 
    } 
}; 

Однако этот подход имеет довольно много потерь производительности.

  • Новый список указателей на базовый класс должен быть создан для каждого списка указателей на производный класс. Новый график содержит много времени и памяти.
  • Доступ к объектам осуществляется с помощью указателей. Эта косвенность замедляет работу.
  • Поскольку объекты не распределены в компактную структуру данных, промахи кэша будут серьезными.

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

+0

Я нахожу это очень интересным .... тем не менее, он фактически не преобразовывает его в shared_ptr базового класса, поэтому теперь это приводит к тому, что реализация «процесса» помещается в заголовочный файл (что-то, чего я хочу избежать) , Я как-то схожу с этим здесь http://coliru.stacked-crooked.com/a/571ec96419f8224d, и он передает его еще как 'std :: vector >' В моем случае я имею дело с потенциально 100 миллионов членов этого вектора, и я действительно хочу избежать их копирования. Я бы использовал фактический базовый класс, но мне нужно всего лишь несколько дополнительных свойств. – user3072517

+0

@ user3072517 Я получил ответ в ответ. –

+0

Похоже, что есть не так много способов сделать копию, если я хочу сохранить наследование. Кажется, такой стыд, что это невозможно сделать без такого удара производительности. Чтобы лучше понять, почему его спрашивают, это потому, что эти векторы содержат все поля в каждой записи из результирующего набора запроса. В производных версиях они содержат флаг «Изменено». Они создаются как производный набор, но теперь мне нужно загрузить их с ответом из базы данных. Эта загрузка происходит в базовом классе, и я предпочел бы не повторять логику нагрузки производного класса. – user3072517

Смежные вопросы