Виртуальный шаблонный обходной путь без слишком многословия - PullRequest
0 голосов
/ 08 апреля 2019

Я ищу обходной путь к отсутствию виртуальных шаблонных функций в C ++.В идеале я хочу сохранить свои производные классы в векторе, выполнить итерацию по ним и вызвать правильную функцию, поэтому в псевдокоде:


template<typename T>
struct Output
{
    ...
};

struct Base
{
    template<typename T>
    virtual void doSomething(Output<T>& out) = 0;
};

struct DerivedA : public Base
{
    DerivedA(const char* filename) {...}
    template<typename T>
    void doSomething(Output<T>& out) final
    {
        ...
    }
};

struct DerivedB : public Base
{
    DerivedB(const char* filename) {...}
    template<typename T>
    void doSomething(Output<T>& out) final
    {
        ...
    }
};

int main()
{
    std::vector<Base*> vec;
    vec.push_back(new DerivedA("data1.bin"));
    vec.push_back(new DerivedB("data2.bin"));
    vec.push_back(new DerivedA("data3.bin"));
    vec.push_back(new DerivedA("data4.bin"));

    Output<float> outF;
    Output<double> outD;
    Output<int> outI;
    for (auto e : vec)
    {
        e->doSomething(outF);
        e->doSomething(outD);
        e->doSomething(outI);
    }

    return 0;
}

Я бы предпочел, чтобы обходной путь был«безболезненный» и не многословный, насколько это возможно (поскольку я использую шаблоны, чтобы избежать переопределения одной и той же функции n раз для n различных типов в первую очередь).Я думал о том, чтобы сделать себя vtable с помощью std :: map и сделать несколько dynamic_casts.Я ищу какие-нибудь лучшие идеи или даже краткую реализацию этой идеи, если вы считаете ее лучшей в этом сценарии.Я ищу решение, которое в идеале должно быть наименее навязчивым, и к которому очень легко добавлять новые классы.

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

struct Base
{
    virtual void doSomething(Output<int>& out) = 0;
    virtual void doSomething(Output<float>& out) = 0;
    virtual void doSomething(Output<double>& out) = 0;

private:
    template<typename T>
    void doSomething(Output<T>& out)
    {
        std::cout << "Base doSomething called with: " << typeid(T).name() << "\n";
    }
};

struct DerivedA : public Base
{
    void doSomething(Output<int>& out) final
    {
        doSomething<int>(out);
    }
    void doSomething(Output<float>& out) final
    {
        doSomething<float>(out);
    }
    void doSomething(Output<double>& out) final
    {
        doSomething<double>(out);
    }
private:
    template<typename T>
    void doSomething(Output<T>& out)
    {
        std::cout << "DerivedA doSomething called with: " << typeid(T).name() << "\n";
    }
};

struct DerivedB : public Base
{
    void doSomething(Output<int>& out) final
    {
        doSomething<int>(out);
    }
    void doSomething(Output<float>& out) final
    {
        doSomething<float>(out);
    }
    void doSomething(Output<double>& out) final
    {
        doSomething<double>(out);
    }
private:
    template<typename T>
    void doSomething(Output<T>& out)
    {
        std::cout << "DerivedB doSomething called with: " << typeid(T).name() << "\n";
    }
};

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

1 Ответ

0 голосов
/ 08 апреля 2019

Попробуйте что-то вроде этого:

struct OutputBase
{
    virtual void doSomething() = 0;
};

template<class T >
struct Output : public OutputBase
{
    virtual void doSomething()
    {
        std::cout << typeid(T).name();
    }
};


struct Base
{
    virtual void doSomething(OutputBase* out) = 0;
};

struct DerivedA : public Base
{
    virtual void doSomething(OutputBase* out)
    {
        std::cout << "DerivedA doSomething called with: ";
        out->doSomething();
        std::cout<< std::endl;
    }
};

struct DerivedB : public Base
{
    virtual void doSomething(OutputBase* out)
    {
        std::cout << "DerivedB doSomething called with: ";
        out->doSomething();
        std::cout << std::endl;
    }
};
int main()
{
    OutputBase* out_int = new Output < int > ;
    OutputBase* out_double = new Output < double >;
    Base* a = new DerivedA;
    a->doSomething(out_int);
    a->doSomething(out_double);
    Base* b = new DerivedB;
    b->doSomething(out_int);
    b->doSomething(out_double);

    return 0;
}

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

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