У меня есть код как ниже. У меня есть абстрактный шаблонный класс Foo и два подкласса (Foo1 и Foo2), которые происходят из инстанцирований шаблона. Я хочу использовать указатели в своей программе, которая может указать на любой на объекты типа Foo1 или Foo2, следовательно я создал интерфейс IFoo.
Моя проблема, я не уверен, как включать functionB в интерфейс, так как это - иждивенец на шаблонном инстанцировании. Даже возможно сделать 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>{
...
};
Поскольку тип аргумента функцииВ должен быть известен заранее, у вас есть только один выбор: Сделать его типом, который может содержать все возможные аргументы. Это иногда называют "верхним типом", и в библиотеках 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
выбрасывает исключение, потому что даже не пытается преобразовать целое число 123 в двойное. If не заботится о преобразованиях, потому что все равно невозможно воспроизвести их все. Таким образом, есть несколько ограничений, но при необходимости можно найти обходные пути.
Самый простой способ - создать интерфейс по шаблону.
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; };
};
Вы действительно пытаетесь сделать невозможное.
Суть дела проста: виртуальный
и шаблон
плохо сочетаются друг с другом.
шаблон
предназначен для генерации кода во время компиляции. Вы можете думать об этом как о макросах с распознаванием типов + несколько приколов для метапрограммирования. virtual
касается решения во время выполнения, и это требует некоторой работы. virtual
обычно реализуется с использованием виртуальных таблиц (подумайте о таблице, в которой перечислены методы). Количество методов должно быть известно во время компиляции и определяется в базовом классе.
Однако с вашим требованием нам понадобится виртуальная таблица бесконечного размера, содержащая методы для типов, которые мы еще не видели и которые будут определены только в ближайшие годы ... это, к сожалению, невозможно.
А если бы это было возможно?
Ну, это просто не имело бы смысла. Что происходит, когда я вызываю Foo2
с int
? Это не предназначено для этого! Следовательно, это нарушает принцип, согласно которому Foo2
реализует все методы из IFoo
.
Итак, было бы лучше, если бы вы указали реальную проблему, таким образом мы могли бы помочь вам на уровне дизайна, а не на техническом уровне :)
Я не думаю, что вы можете получить то, что хотите. Подумайте об этом, если бы вы реализовали свое предложение: если у вас есть указатель на экземпляр IFoo
и вы вызываете functionB ()
, какой параметр типа вы должны ему указать? Основная проблема заключается в том, что Foo1 :: functionB
и Foo2 :: functionB
имеют разные подписи и выполняют разные функции.