Создание интерфейса для шаблона абстрактного класса в С++
У меня есть код, как показано ниже. У меня есть абстрактный шаблонный класс 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>{
...
};
Ответы
Ответ 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
имеют разные подписи и делают разные вещи.