webentwicklung-frage-antwort-db.com.de

Virtuelle C++ - Vorlagenmethode

Ich habe eine abstrakte Klasse (ich weiß, dass es nicht so kompilieren wird, aber um zu verstehen, was ich machen möchte):

class AbstractComputation {
    public:
        template <class T> virtual void setData(std::string id, T data);
        template <class T> virtual T getData(std::string id);
};

class Computation : public AbstractComputation {
    public:
        template <class T> void setData(std::string id, T data);
        template <class T> T getData(std::string id, T data);
};

Wenn ich also setData<double>("foodouble", data) anrufe, möchte ich, dass das durch foodouble identifizierte Double (interner Mechanismus, der hier nicht das Hauptanliegen ist) auf die Double-Daten gesetzt wird.

Also, wie geht das? 

Ich denke, dass es vielleicht einen Mittelwert gibt, wenn ich etwas wie virtual void setData<double>(std::string id, double data) eingebe, aber ich weiß nicht, wie ich das machen soll.

28
Vincent

Das Problem ist, dass Sie statischen Zeitpolymorphismus (Vorlagen) nicht einfach mit Laufzeitpolymorphismus mischen können. Der Grund dafür, dass die Sprache das bestimmte Konstrukt in Ihrem Beispiel nicht zulässt, besteht darin, dass möglicherweise unendlich viele verschiedene Typen vorhanden sind, die Ihre Template-Member-Funktion instanziieren könnten. Dies bedeutet, dass der Compiler Code generieren muss, um diese vielen Typen dynamisch zu verteilen ist nicht machbar.

Es gibt verschiedene Dinge, die hier gemacht werden können, um die Begrenzung zu umgehen, im Grunde genommen entweder den statischen oder den dynamischen Polymorphismus. Um den dynamischen Polymorphismus aus der Gleichung zu entfernen, können Sie einen Typ angeben, von dem nicht abgeleitet wird, um die <key,value>-Zuordnungen zu speichern, und dann die Vorlage anbieten, die nur auf der Basisebene aufgelöst wird:

class AbstractComputation {
public:
   template <typename T>
   void setData( std::string const & id, T value ) {
      m_store.setData( id, value );
   }
   template <typename T>
   T getData( std::string const & id ) const {
      return m_store.getData<T>( id );
   }
protected:
   ValueStore m_store;
};

Abgeleitete Klassen können jetzt von der Basis aus auf die Variable ValueStore zugreifen, und es ist kein Polymorphismus erforderlich. (Dies kann auch durch direktes Implementieren der Funktionalität in AbstractComputation geschehen, aber es ist wahrscheinlich sinnvoll, die Bedenken zu trennen.)

Die andere Option besteht darin, den Laufzeitpolymorphismus beizubehalten, statischen Polymorphismus jedoch zu entfernen. Dies kann durch Ausführen der Typlöschung für die Basisklasse und anschließende Weiterleitung an die entsprechende (nicht mit Vorlagen versehene) Funktion ausgeführt werden, die die Typ-gelöschten Argumente akzeptiert. Die einfachste Version davon ist nur boost::any:

class AbstractComputation {
public:
   template <typename T>
   void setData( std::string const & id, T value ) {
      setDataImpl( id, boost::any( value ) );
   }
   template <typename T>
   T getData( std::string const & id ) const {
      boost::any res = getDataImpl( id );
      return boost::any_cast<T>( res );
   }
protected:
   virtual void setDataImpl( std::string const & id, boost::any const & value ) = 0;
   virtual boost::any getDataImpl( std::string const & id ) const = 0;
};

Wie das Löschen von Typen unter der Haube implementiert wird, ist interessant, aber außerhalb des Geltungsbereichs ist es wichtig, dass ein boost::any ein konkreter (nicht mit Vorlagen versehener) Typ ist, der any - Typ intern speichern kann, indem er den Typ löscht Argumente und ermöglicht gleichzeitig den typsicheren Abruf der Daten.

In einigen Fällen kann es ausreichend sein, die Vorlage von der Methodenebene zur Klassenebene zu verschieben, z.

#include <iostream>

template<typename T>
class AbstractComputation {
public:
    virtual void setData(std::string id, T data)
    {
        std::cout << "base" << std::endl;
    }
};

template<typename T>
class Computation : public AbstractComputation<T> {
public:
    virtual void setData(std::string id, T data)
    {
        std::cout << "derived" << std::endl;
    }
};

int main()
{
    AbstractComputation<int> *x = new Computation<int>();

    x->setData("1", -1);

    delete x;
    return 0;
}
14
TobiMcNamobi

Erstens können Sie keine virtual Vorlagenfunktionen haben. Da Vorlagen zur Kompilierzeit aufgelöst werden, funktioniert virtual nicht, da der Compiler nicht weiß, welche Vorlage ausgewählt werden soll. Siehe hier für weitere Informationen dazu.

2
Tony The Lion

Verwenden Sie boost::any, um das Datum zu akzeptieren. Wenn Sie das Datum tatsächlich festlegen, greifen Sie den richtigen Typ aus dem Datum heraus.

1
Nim

In Ihrem Fall können Sie wahrscheinlich boost::any verwenden.

virtual void setData(std::string id, boost::any data);

Es ist ein Wrapper, der fast alles einkapseln kann.

Weitere Informationen zu einem ähnlichen Thema in dieser Antwort .

1
jpalecek

Wenn Sie im Voraus eine Liste der möglichen Typen kennen, kann der Präprozessor helfen:

#define MY_CLASSES MYTYPE(int) MYTYPE(float) MYTYPE(double)

class AbstractComputation {
    public:
#     define MYTYPE(T) virtual void setData(std::string id, T data)=0;\
                       virtual void getData(std::string id, T& dst_data)=0;
      MY_CLASSES
#     undef MYTYPE
};

class Computation : public AbstractComputation {
    public:
#     define MYTYPE(T) virtual void setData(std::string id, T data){std::cout<<"writing: "<<data<<std::endl;}\
                       virtual void getData(std::string id, T& dst_data){dst_data=0;/*put your actual implementation here*/}
      MY_CLASSES
#     undef MYTYPE
};

Wenn Sie möglicherweise keine vollständige Liste möglicher Typen kennen, ist Ihr Problem nicht lösbar. Das Löschen von Typen kann, wie von anderen erwähnt, auch helfen .. aber nicht unter allen Umständen.

1
user396672