Подтвердить что ты не робот

Создание интерфейса для шаблона абстрактного класса в С++

У меня есть код, как показано ниже. У меня есть абстрактный шаблонный класс Foo и два подкласса (Foo1 и Foo2), которые берутся из экземпляров шаблона. Я хочу использовать указатели в своей программе, которые могут указывать на объекты типа Foo1 или Foo2, поэтому я создал интерфейс IFoo.

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

Большое спасибо за вашу помощь.

class IFoo {
    public:
        virtual functionA()=0;

};

template<class T>
class Foo : public IFoo{
    public:
        functionA(){ do something; };
        functionB(T arg){ do something; };
};

class Foo1 : public Foo<int>{
...
};

class Foo2 : public Foo<double>{
...
};
4b9b3361

Ответ 1

На самом деле вы пытаетесь сделать невозможное.

Сама суть дела проста: virtual и template не смешиваются хорошо.

  • template - это генерация кода времени компиляции. Вы можете подумать об этом как о некоторых типах макросов, поддерживающих тип, и о нескольких приправленных трюках для метапрограмм.
  • virtual - это решение во время выполнения, и для этого требуется некоторая работа.

virtual обычно реализуется с использованием виртуальных таблиц (подумайте о таблице, в которой перечислены методы). Количество методов должно быть известно во время компиляции и определено в базовом классе.

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

И если бы это было возможно?

Ну, это просто не имеет смысла. Что происходит, когда я вызываю Foo2 с int? Это не для этого! Поэтому он нарушает принцип, согласно которому Foo2 реализует все методы из IFoo.

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

Ответ 2

Самый простой способ - сделать ваш интерфейс шаблоном.

template <class T>
class IFoo {
    public:
        virtual void functionA()=0;
        virtual void functionB(T arg){ do something; };
};

template<class T>
class Foo : public IFoo<T>{
    public:
        void functionA(){ do something; };
        void functionB(T arg){ do something; };
};

Ответ 3

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

#include <boost/any.hpp>
#include <iostream>
using namespace boost;

class IFoo {
    public:
    virtual void functionA()=0;
    virtual void functionB(any arg)=0; //<-can hold almost everything
};

template<class T>
class Foo : public IFoo{
    public:
        void functionA(){  };
        void real_functionB(T arg)
        {
         std::cout << arg << std::endl;
        };
        // call the real functionB with the actual value in arg
        // if there is no T in arg, an exception is thrown!

        virtual void functionB(any arg)
        {
            real_functionB(any_cast<T>(arg));
        }
};

int main()
{
    Foo<int> f_int;
    IFoo &if_int=f_int;

    if_int.functionB(10);

    Foo<double> f_double;
    IFoo &if_double=f_double;
if_int.functionB(10.0);

}

К сожалению, any_cast не знает о обычных преобразованиях. Например, any_cast<double>(any(123)) генерирует исключение, потому что оно даже не пытается преобразовать целое число 123 в double. Если это не касается конверсий, потому что все равно невозможно воспроизвести их всех. Таким образом, существует несколько ограничений, но при необходимости можно найти обходные пути.

Ответ 4

Я не думаю, что вы можете получить то, что хотите. Подумайте об этом, если бы вы выполнили свое предложение: если у вас есть указатель на экземпляр IFoo, и вы вызываете functionB(), какой параметр типа вы должны его дать? Основная проблема заключается в том, что Foo1::functionB и Foo2::functionB имеют разные подписи и делают разные вещи.