2017-09-07 2 views
17

J'écris en C++ et je veux passer un type inconnu (connu seulement dans le temps d'exécution) à une fonction virtuelle pure:C++ passage type inconnu à une fonction virtuelle

virtual void DoSomething(??? data); 

DoSomething est une mise en œuvre d'une fonction virtuelle pure dans une classe dérivée.

je comptais utiliser des modèles mais tourner en fonction virtuelle et modèles ne fonctionnent pas ensemble: Can a C++ class member function template be virtual?

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

Merci à l'avance

+1

pouvez-vous s'il vous plaît être plus précis? Comment utilisez-vous les données dans la fonction? Quelles sont les exigences du type pour les données? Par exemple. y a-t-il seulement quelques classes connues, ou prévoyez-vous d'accepter une classe qui a une méthode spécifique, etc.? – bolov

+1

Votre question doit être réduite un peu. Connaissez-vous la gamme de types, voulez-vous qu'ils soient automatiquement déduits? La réponse la plus simple serait "use void *", une meilleure réponse pourrait être celle de Story Teller. Tout dépend de votre cas. –

+0

Je suis curieux, en regardant les réponses, à propos de la possibilité d'obtenir le même résultat sans avoir à faire de casting sur 'doSomething'. Quelque chose comme encapsuler le type d'une certaine façon sans avoir à faire 'doSomething' un modèle, et récupérer ce type avec un' decltype', c'est-à-dire Type d'une méthode d'usine virtuelle ... – perencia

Répondre

18

Vous devez type erasure. Un exemple de ceci est l'objectif général boost::any (et std::any en C++ 17).

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

Et puis chaque sous-classe peut tenter la sécurité any_cast afin d'obtenir les données qu'il 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 rouler votre propre abstraction d'effacement de type si la gamme de comportements que vous cherchez est plus restreint.

0

quelque chose comme ça ?:

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; 
} 
+6

'Foo' a besoin d'au moins une fonction membre virtuelle (probablement le destructeur), sinon' dynamic_cast' ne fonctionnera pas. – Angew

+0

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

+0

Cela ne nécessite pas de classe de base pour chaque objet passé dans la fonction, seulement pour le wrapper ' Bar'. Bien que techniquement, il s'agit de l'objet transmis, il est peu probable que ce soit ce que l'OP voulait dire. – AzCopey

1

Si vous ne souhaitez pas utiliser boost/C++ 17 tout, pensez à dériver le paramètre de la fonction « doSometing » d'une classe de base, et ne la chassez dynamique le bon objet de classe. Dans ce cas, vous pouvez vérifier à l'exécution que vous avez 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. 
} 

Le plus simple (mais dangereux!) Façon serait d'utiliser void * pointeur + cast 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

type effacement n'est pas la seule possibilité.

Vous pouvez être intéressé à utiliser le modèle des visiteurs: prendre comme argument une std :: variante et visiter avec un lambda contenant le code de modèle que vous vouliez mettre en œuvre:

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