С++ передает неизвестный тип виртуальной функции
Я пишу на С++ и хочу передать неизвестный тип (известный только во время выполнения) чистой виртуальной функции:
virtual void DoSomething(??? data);
где DoSomething
- реализация чистой виртуальной функции в производном классе.
Я планировал использовать шаблоны, но, как оказалось, виртуальная функция и шаблоны не работают вместе: Может ли шаблон функции члена класса С++ быть виртуальным?
Я хочу избежать использования базового класса для всех классов, которые я передаю функции (что-то вроде object в С#).
Заранее спасибо
Ответы
Ответ 1
Вам нужно удалить стирание. Примером этого является общая цель boost::any
(и std::any
в С++ 17).
virtual void DoSomething(boost::any const& data);
И тогда каждый подкласс может попытаться безопасный any_cast
, чтобы получить ожидаемые данные.
void DoSomething(boost::any const& data) {
auto p = any_cast<std::string>(&data);
if(p) {
// do something with the string pointer we extracted
}
}
Вы можете, конечно, развернуть свой собственный стирающий абстракцию, если диапазон поведения, который вы ищете, более ограничен.
Ответ 2
Если вы не хотите использовать boost/С++ 17 any, подумайте о том, чтобы получить параметр функции doSometing из базового класса и сделать динамический приведение к правильному объекту класса. В этом случае вы можете проверить во время выполнения, что у вас есть действительный указатель.
class param{
public:
virtual ~param(){};
};
template <typename T>
struct specificParam:param{
specificParam(T p):param(p){}
T param;
};
class Foo
{
public:
virtual void doSomething(param* data) = 0;
};
template <typename T>
class Bar : public Foo
{
public:
virtual void doSomething(param* data){
specificParam<T> *p = dynamic_cast<specificParam<T> *>(data);
if (p != nullptr){
std::cout<<"Bar got:" << p->param << "\n";
}
else {
std::cout<<"Bar: parameter type error.\n";
}
}
};
int main(){
Bar<char> obj1;
Bar<int> obj2;
Bar<float> obj3;
specificParam<char> t1('a');
specificParam<int> t2(1);
specificParam<float> t3(2.2);
obj1.doSomething(&t1); //Bar got:a
obj2.doSomething(&t2); //Bar got:1
obj3.doSomething(&t3); //Bar got:2.2
// trying to access int object with float parameter
obj2.doSomething(&t3); //Bar: parameter type error.
}
Простейшим (но небезопасным!) способом было бы использовать указатель void * static static
class Foo
{
public:
virtual void doSomething(void* data) = 0;
};
template <typename T>
class Bar:public Foo
{
public:
virtual void doSomething(void* data){
T* pData = static_cast<T*>(data);
std::cout<<"Bar1 got:" << *pData << "\n";
}
};
int main(){
Bar<char> obj1;
Bar<int> obj2;
Bar<float> obj3;
char c = 'a';
int i = 1;
float f = 2.2;
obj1.doSomething(&c); // Bar1 got:a
obj2.doSomething(&i); // Bar1 got:1
obj3.doSomething(&f); // Bar1 got:2.2
//obj2.doSomething(&c); // Very bad!!!
}
Ответ 3
Тип-стирание - это не единственная возможность.
Вам может быть интересно использовать шаблон посетителя: возьмите в качестве аргумента std:: variant и посетите его с помощью лямбда, содержащей код шаблона, который вы хотели реализовать:
virtual void doSomething(std::variant<int,float/*,...*/> data)
{
visit([=](auto v){/*...*/;},data);
}
Ответ 4
что-то вроде этого:
class Foo
{
virtual ~Foo() = 0;
};
template <typename T>
class Bar : public Foo
{
T object;
}
...
virtual void DoSomething(Foo* data)
{
Bar<int>* temp = dynamic_cast<Bar<int>*>(data);
if (temp)
std::count<<temp->object;
}