it-swarm.com.de

C++ übergibt einen unbekannten Typ an eine virtuelle Funktion

Ich schreibe in C++ und möchte einen unbekannten Typ (nur zur Laufzeit bekannt) an eine reine virtuelle Funktion übergeben:

virtual void DoSomething(??? data);

dabei ist DoSomething eine Implementierung einer rein virtuellen Funktion in einer abgeleiteten Klasse.

Ich hatte vor, Vorlagen zu verwenden, aber wie sich herausstellt, funktionieren virtuelle Funktionen und Vorlagen nicht zusammen: Kann eine C++ - Klassenmitgliedsfunktionsvorlage virtuell sein?

Ich möchte vermeiden, eine Basisklasse für alle Klassen zu verwenden, die ich an die Funktion übergebe (etwas wie object in C #).

Danke im Voraus

17
Avner Gidron

Sie müssen Typ löschen . Ein Beispiel dafür ist der allgemeine Zweck boost::any (und std::any in C++ 17).

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

Und dann kann jede Unterklasse den safeany_cast verwenden, um die erwarteten Daten zu erhalten.

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

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

Sie können natürlich Ihren eigenen Typ zum Löschen der Abstraktion ausrollen, wenn der von Ihnen gesuchte Verhaltensbereich stärker eingeschränkt ist.

18
StoryTeller

Das Löschen von Typen ist nicht die einzige Möglichkeit.

Das Besuchermuster kann von Interesse sein: Nehmen Sie eine std :: variant als Argument und besuchen Sie es mit einem Lambda, der den Code enthält, den Sie implementieren möchten:

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

Wenn Sie boost/C++ 17 keines verwenden möchten, ziehen Sie in Betracht, den Parameter der 'doSometing'-Funktion von einer Basisklasse abzuleiten, und nehmen Sie eine dynamische Umwandlung in das richtige Klassenobjekt vor. In diesem Fall können Sie in Runtime prüfen, ob Sie einen gültigen Zeiger haben. 

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

Die einfachste (aber unsichere!) Methode wäre die Verwendung von void * pointer + static cast 

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

sowas in der Art?:

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