Проверьте на равенство класса шаблона через указатель базового класса - PullRequest
0 голосов
/ 09 февраля 2019

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

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

class A{}

class B_base : public A{}

template<T>
class B : public B_base {}

// There may be other derived classes of A
template<T>
class C: public A{}

void main() {
    // ... some vector of pointers to A derived objects
    std::vector<A*> v;

    for(auto& i : v){
        // Check whether i is any specialization of B through a 
        // dynamic_cast to the intermediate class
        if(dynamic_cast<B_base*>()){
            // This is a B_base object, 
        }
    }
}

В идеале, я хотел бы что-то подобное, чтобы избежать промежуточного класса.

class A{}

template<T>
class B : public A{}

// There may be other derived classes of A
template<T>
class C: public A{}

void main() {
    // ... some vector of pointers to A derived objects
    std::vector<A*> v;

    for(auto& i : v){
        // Check whether i is any specialization of B
        if(templateTypeId(i) == templateTypeId(B*)){
            // This is a B object with some unknown specialization
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 10 февраля 2019

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

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

class A {
public:
    virtual ~A() = default;
    virtual bool is_B() const noexcept { return false; }
};

template <class T>
class B : public A {
public:
    bool is_B() const noexcept override { return true; }
};

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

Другой идеей было бы связать проверку типа с адресом объекта:

struct type_tag {
    constexpr type_tag() = default;
    type_tag(const type_tag&) = delete;
    type_tag& operator=(const type_tag&) = delete;
};

class A {
public:
    virtual ~A() = default;
    virtual bool matches_type(const type_tag&) const
    { return false; }
};

inline constexpr type_tag B_tag{};

template <class T>
class B {
public:
    bool matches_type(const type_tag& tag) const override
    { return &tag == &B_tag; }
};

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

0 голосов
/ 09 февраля 2019

Может быть, лучше будет добавить необходимые виртуальные функции в интерфейс A, чтобы вы могли вызывать их непосредственно на A*, не угадывая производный класс.Последний является анти-паттерном, потому что он отрицает цель полиморфизма: идею о том, что кусок кода может работать с объектами разных классов, не зная их точного типа.Вы также можете помещать объекты разных типов в разные контейнеры и вообще не использовать ploymorphism, основанный на виртуальных функциях.

...