web-dev-qa-db-fra.com

C++ passant un type inconnu à une fonction virtuelle

J'écris en C++ et je veux passer un type inconnu (connu uniquement en exécution) à une fonction virtuelle pure:

virtual void DoSomething(??? data);

DoSomething est une implémentation d'une fonction virtuelle pure dans une classe dérivée.

J'avais prévu d'utiliser des modèles, mais comme il s'avère que les fonctions virtuelles et les modèles ne fonctionnent pas ensemble: Un modèle de fonction de membre de classe C++ peut-il être virtuel?

Je veux éviter d'utiliser une classe de base pour toutes les classes que je passe à la fonction (quelque chose comme object en C #).

Merci d'avance

17
Avner Gidron

Vous avez besoin de type erasure . Un exemple de ceci est l’usage général boost::any (et std::any en C++ 17).

virtual void DoSomething(boost::any const& data);

Ensuite, chaque sous-classe peut essayer le safeany_cast afin d’obtenir les données qu’elle attend.

void DoSomething(boost::any const& data) {
  auto p = any_cast<std::string>(&data);

  if(p) {
    // do something with the string pointer we extracted
  }
}

Vous pouvez bien sûr déployer votre propre type en effaçant l’abstraction si la gamme de comportements que vous recherchez est plus limitée.

18
StoryTeller

L’effacement des caractères n’est pas la seule possibilité.

Vous pouvez être intéressé par le motif visiteur: prenez comme argument un std :: variant et visitez-le avec un lambda contenant le code de modèle que vous vouliez implémenter:

virtual void doSomething(std::variant<int,float/*,...*/> data)
   {
   visit([=](auto v){/*...*/;},data);
   }
1
Oliv

Si vous ne souhaitez pas utiliser boost/C++ 17, pensez à dériver le paramètre de la fonction 'doSometing' à partir d'une classe de base et effectuez un transtypage dynamique vers le bon objet. Dans ce cas, vous pouvez vérifier au moment de l'exécution que vous disposez d'un pointeur valide. 

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.
}

La manière la plus simple (mais dangereuse!) Serait d'utiliser void * pointeur + distribution statique 

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!!!     
}
1
Nir

quelque chose comme ca?:

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;
}
0
gotocoffee