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

С++ передает неизвестный тип виртуальной функции

Я пишу на С++ и хочу передать неизвестный тип (известный только во время выполнения) чистой виртуальной функции:

virtual void DoSomething(??? data);

где DoSomething - реализация чистой виртуальной функции в производном классе.

Я планировал использовать шаблоны, но, как оказалось, виртуальная функция и шаблоны не работают вместе: Может ли шаблон функции члена класса С++ быть виртуальным?

Я хочу избежать использования базового класса для всех классов, которые я передаю функции (что-то вроде object в С#).

Заранее спасибо

4b9b3361

Ответ 1

Вам нужно удалить стирание. Примером этого является общая цель boost::anystd::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;
}