Как преобразовать шаблон класса в зависимости от типа аргумента шаблона - PullRequest
0 голосов
/ 28 июня 2019

В настоящее время я храню указатели разных типов в векторе.Чтобы архивировать это, я реализовал шаблон класса «Store», который является производным от неклассового шаблона «IStore».Мой вектор наконец хранит указатели на «IStore».В коде:

class IStore
{
public:
    IStore() = default;
    virtual ~IStore() = default;

    virtual void call() = 0;
    // ... other virtual methods
};

template<typename T>
class Store : public IStore
{
public:
    Store() = default;
    virtual ~Store() = default;

    virtual void call() override;
    // ... other virtual methods

private:
    T* m_object = nullptr;
}

И в моем основном классе, который содержит вектор:

class Main
{
public:
    template<typename T>
    void registerObject(T* ptr);

    template<typename T>
    void callObjects();
    // ... other methods

private:
    std::vector<IStore*> m_storedObjects;
};

Пока что структура текущего класса.Чтобы описать проблему, мне нужно представить следующие три примера структур:

struct A {}
struct B : public A {}
struct C : {}

Другие классы должны вызывать метод Main :: registerObject с указателями на объекты типов A, B или C.Этот метод затем создаст новый магазин , магазин соответственно.Сохраните объект класса шаблона и вставьте указатель на этот объект в m_storedObjects.

Теперь начинается сложная часть: метод Main :: callObjects должен вызываться другими классами с аргументом шаблона, таким как Main :: callObjects ().Это должно выполнить итерацию через m_storedObjects и вызвать метод IStore :: call для каждого объекта, который относится к типу B или к которому относится тип B.

Например:

Main::registerObject<A>(obj1);
Main::registerObject<B>(obj2);
Main::registerObject<C>(obj3);
Main::callObjects<B>();

Следует вызватьobj1 и obj2, но не obj3, потому что C не является B, а B не получен из C.

Мои подходы в Main :: callObjects были следующими: 1. Выполнить dynamic_cast и проверить на nullptr, например:

for(auto store : m_storedObjects)
{
    Store<T>* base = dynamic_cast<Store<T>*>(store);
    if(base)
    {
        // ...
    }
}

, который будет работать только для тех же классов, а не производных классов, поскольку Store не является производным от Store .2. Чтобы переписать оператор приведения в IStore и Store, чтобы я мог указать, что Store должен быть преобразуемым, когда аргумент шаблона является преобразуемым.Например в Store:

template<typename C>
operator Store<C>*()
{
    if(std::is_convertible<T, C>::value)
    {
        return this;
    }
    else
    {
        return nullptr;
    }
}

Но этот метод никогда не вызывается.

У кого-нибудь есть решение этой проблемы?Извините за длинный пост, но я думал, что больше кода будет лучше понять проблему.В любом случае спасибо за вашу помощь:)

1 Ответ

0 голосов
/ 28 июня 2019

Подумав, я понял, что ваше стирание типов, от присвоения Store<T> объектов указателям IStore*, делает невозможным использование какой-либо проверки типов во время компиляции, такой как std::is_base_of и тому подобное.Следующий лучший вариант - информация о типе во время выполнения (dynamic_cast<>(), typeid()).Как вы заметили, dynamic_cast<>() не может определить, является ли тип объекта предком другого типа, только если тип объекта является потомком другого типа, известного во время компиляции.

РЕДАКТИРОВАТЬ: С поддержкой C ++ 17 я могу придумать другой способ решения вашей проблемы, основанный на std::visit примере здесь .Если вы измените свой Main интерфейс ...

#include <iostream>
#include <vector>
#include <variant>

template <typename T>
class Store {
public:
    using value_type = T;

    Store(T* object): m_object(object) {}

    void call() { std::cout << "Hello from " << typeid(T).name() << '\n'; }
    // ... other methods

private:
    T* m_object = nullptr;
};



template <typename... Ts>
class Main {
private:
    std::vector<std::variant<Store<Ts>...>> m_storedObjects;

public:
    // replacement for registerObjects, if you can take all objects in at once
    Main(Ts*... args): m_storedObjects({std::variant<Store<Ts>...>(Store<Ts>{args})...}) {}

    template <typename U>
    void callObjects() {
        for (auto& variant : m_storedObjects) {
            std::visit([](auto&& arg) {
                using T = typename std::decay_t<decltype(arg)>::value_type;
                if constexpr (std::is_base_of<T, U>::value) {
                    arg.call();
                }
            }, variant);
        } 
    }

};


struct A {};
struct B : public A {};
struct C {};

int main() {
    A a;
    B b;
    C c;
    auto m = Main{&a, &b, &c};
    m.callObjects<B>();
    // > Hello from 1A
    // > Hello from 1B
    return 0; 
}

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