Можно ли привести функцию из вектора parent * в vector child * без использования шаблонов? - PullRequest
0 голосов
/ 27 апреля 2018

Работая над системой импорта данных, я решил сохранить множество объектов, производных от одного класса, в векторе указателей на родительский класс. И затем я хотел бы иметь функцию, которая возвращает вектор любого типа дочерних указателей (используя параметры, которые позволяют мне знать, что это за дочерний элемент).

Мне удалось реализовать подобный и упрощенный код здесь, но он использует шаблоны и приведение, и я чувствую, что только приведений может быть достаточно. Однако компилятор не хочет выполнять преобразование из вектора A * в вектор B *.

РЕДАКТИРОВАТЬ: В реальном коде есть много дочерних классов, не только B, поэтому замена шаблона на B не вариант, извините за недостаточную точность.

#include <vector>

using namespace std;

class A
{
  public:

    int attr;

    A(): attr(1) {}
};

class B : public A
{
  public:

    B(): A() {attr = 2;}
};

template <typename O>
vector<O*> bees(vector<A*> vecA)
{
    auto vecO = vector<O*>();

    for (auto it = vecA.begin(); it != vecA.end(); it++)
    {
        if ((*it)->attr == 2)
        {
            vecO.push_back(reinterpret_cast<O*>(*it));
        }
    }

    return vecO;
}

int main()
{
    auto vecA = vector<A*>();

    vecA.push_back(new A());
    vecA.push_back(new B());
    vecA.push_back(new B());

    vector<B*> vecB = bees<B>(vecA);
}

Итак, мой вопрос: возможно ли, чтобы код делал такой же эффект без использования шаблонов? А если нет, то компилятор генерирует определенный код с этим? Знание, что теоретически не будет никакой разницы во времени выполнения, независимо от шаблона.

Спасибо.

Ответы [ 3 ]

0 голосов
/ 27 апреля 2018

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

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

Если вы хотите отфильтровать вектор, чтобы получить только объекты с определенными свойствами, вы не должны смотреть на их типы, вы должны спросить их, есть ли у них свойства, которые вы ищете:

void FilterA(const std::vector<A>& source, std::vector<A>& destination, std::function<bool(const A&)> filter) {
    std::copy_if(source.begin(), source.end(), std::back_inserter(destination), filter);
}

Тогда вы можете назвать это так:

std::vector<A> filteredVecA;
FilterA(vecA, filteredVecA, [](const A& a){return a.HasSomePropertyYouCareAbout();});
0 голосов
/ 27 апреля 2018

Вы должны рассмотреть возможность перемещения проверки типа внутри иерархии (для этого используются шаблоны, но не приведения):

class A {
public:
    virtual ~A(); // you _are_ in a hierarchy of objects

    template<typename T>
    virtual T* as() const { return nullptr; }
};

template<typename X>
class Convertible: public A {

    template<typename T>
    virtual T* as() const {
        if constexpr(std::is_same_v<X, T>)
            return this;
        else
            return nullptr;
    }
};

class B: public Convertible<B> {
};

template <typename O>
vector<O*> bees(vector<A*> vecA)
{
    auto vecO = vector<O*>();

    foreach (auto ptr: vecA)
    {
        auto ptrO = ptr->as<O>();
        if (ptrO)
            vecO.push_back(ptrO);
    }
    return vecO;
}

Некоторые баллы:

Комментарий ОП:

Я использую переинтерпретацию приведения, потому что если я не произвожу, он просто не компилируется, и мне кажется, что это наиболее адекватно, зная, что мне не нужно вносить какие-либо изменения в объект.

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

0 голосов
/ 27 апреля 2018

Поскольку вам нужна функция, которая может возвращать вектор любого типа дочернего указателя, я считаю, что шаблон необходим для указания дочернего типа, но некоторые вещи, такие как reinterpret_cast и т. Д., Не нужны, и вот пример реализации:

class A
{
  public:

    int attr;

    A(): attr(1) {}
    virtual ~A() {};
};

class B : public A
{
  public:

    B(): A() {attr = 2;}

};
template<typename T>
vector<T*> bees(const vector<A*> &vecA)
{
    vector<T*> vec;

    for (auto it = vecA.begin(); it != vecA.end(); it++)
    {
        T* ptr = dynamic_cast<T*>(*it);
        if(ptr != nullptr)
        {
          vec.push_back(*it);
        }
    }

    return vec;
}

Мы используем dynamic_cast, потому что мы понижаем родительский тип до дочернего типа. Также для работы dynamic_cast нам нужен виртуальный деструктор / виртуальная функция, поскольку для этого требуется RTTI

...