Поиск определенного типа из списка в C ++ с использованием шаблонов, не включая базовые классы - PullRequest
0 голосов
/ 19 января 2019

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

У меня есть список действий, которые я хочу отслеживать

class Activity {
    public:
        virtual void DoSomething() = 0 ;
};

std::vector<Activity*> activities;

Допустим, у меня естьследующие дочерние классы

class GraphicsActivity : public Activity {
    public: 
        virtual void DoSomething() { }
        void HandleGraphics() { /* do some management */; }
};

class UpdateActivity : public Activity {
    public: 
        virtual void DoSomething() { ; }
};

class PhysicsActivity : public Activity {
     public:
         virtual void DoSomething() { ; }
 };

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

template<typename T> T* GetActivity() {
    for(int i = 0; i < activities.size(); i++) {
        T* cast = dynamic_cast<T*>(activities[i]);
        if(cast != nullptr) {
            return cast;
        }
     }
     return nullptr;
 }

Мы могли бы использовать ее так:

activities.push_back(new GraphicsActivity());
activities.push_back(new PhysicsActivity ());
activities.push_back(new UpdateActivity ());

GraphicsActivity* g = GetActivity<GraphicsActivity>();

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

class 3DGraphicsActivity : public GraphicsActivity {
    public:
        void Handle3DGraphics() { 
            /* Utilize base class function */
            this->HandleGraphics(); 
            /* do some extra work */
        }
 };

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

activities.push_back(new GraphicsActivity3D());
activities.push_back(new GraphicsActivity());
activities.push_back(new PhysicsActivity ());
activities.push_back(new UpdateActivity ());

Но нам нужен оригинальный тип GraphicsActivity, поэтому мы идем, чтобы получить его:

GraphicsActivity* g = GetActivity<GraphicsActivity>();

Мы фактически получим указатель на первую запись в списке, потому что она разделяет базовый класс типа GraphicsActivity.

В этом и заключается проблема: как я могу написать такой список, элементы которого должны иметь общий базовый класс Activity, а также иметь возможность получить точный тип из списка, не попадая в ловушку dynamic_cast, которую мытолько что объяснил?

1 Ответ

0 голосов
/ 19 января 2019

Оператор typeid можно использовать для проверки того, имеет ли полиморфный объект определенный точный тип, наиболее производный от него.

#include <typeinfo>
// ...

template<typename T> T* GetActivity() {
    for(int i = 0; i < activities.size(); i++) {
        auto* act = activities[i];
        if (act != nullptr && typeid(*act) == typeid(T)) {
            return dynamic_cast<T*>(act);
        }
     }
     return nullptr;
 }

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

...