2016-12-04 3 views
1

Haftungsausschluss: Ich fürchte, die kurze Antwort auf meine Frage ist "nicht möglich". Aber da ich kein C++ - Experte bin, dachte ich, ich versuche es immer noch und frage hier, vielleicht gibt es eine Lösung, die mir nicht bewusst ist.Können wir Templates mit nicht-Templates in C++ "umhüllen"?

So habe ich als Templat containerartigen Klasse MyContainer, die intern speichert Daten in einer std::list<T>, wo T den Vorlagentyp-Klasse ist. Das funktioniert gut.

Jetzt möchte ich eine weitere Klasse hinzufügen, die Instanzen dieser Vorlagen-Klasse in einem std::map<std::string, MyContainer> zuordnen muss. Der Compiler fordert mich jedoch auf, den Template-Klassentyp für den Value-Part der Map wie in std::map<std::string, MyContainer<T>> anzugeben. Aber ich würde diese Vorlage hier lieber weglassen, da ich dann auch Vorlagen für die Wrapper-Klasse verwenden müsste.

Also meine Frage: Gibt es eine Möglichkeit zu erreichen, was ich versuche zu tun, den Vorlagen-Typ für die Wrapper-Klasse zumindest in gewissem Maße weglassen? Oder ist das in C++ einfach nicht möglich, weil der Compiler diese Information ohnehin benötigt?

+8

Es gibt nicht so etwas wie "Templat-Klasse". C++ hat Klassenvorlagen. Klassenvorlagen sind keine Klassen. Deine Beschreibung ist bei weitem nicht klar. Bitte zeigen Sie mehr von Ihrem Code. –

Antwort

3

Eine gängige Technik hierfür ist die Vererbung.

Sie machen MyContainer<T> erben von einigen Basisklasse, wie MyBaseContainer. MyBaseContainer ist keine Vorlagenklasse, aber MyContainer ist. Die MyBaseContainer-Klasse verfügt über virtuelle Funktionen, die von der Vorlagenklasse überschrieben werden.

Dann machen Sie Ihre Karte vom Typ std::map<std::string, MyBaseContainer*>. Es wird in der Lage sein, die virtuellen Funktionen auf den Containern aufzurufen, die es speichert, ohne den Schablonentyp jedes einzelnen zu kennen.

So funktioniert std::function.

+0

Ahh, das macht Sinn. Vielen Dank! – Matthias

+1

Das einzige Problem hier ist, dass MyBaseContainer nicht zu viele nützliche virtuelle Funktionen haben kann. –

+0

Wenn Sie unbedingt müssen, können Sie auch 'dynamic_cast *>()' (oder in einigen Fällen sogar 'static_cast') auf' MyBaseContainer'-Zeigern verwenden. Das ist jedoch ein ziemlich hässlicher Entwurf. – microtherion

1

Dies ist möglich, aber ein bisschen schwierig. Wenn ich Sie richtig verstehe, wollen Sie jede MyContainer Klasse innerhalb map gespeichert haben möglicherweise unterschiedliche Template-Spezialisierung (sagen einige halten std::list<int>, während andere halten) dann Wert Typ für Karte kann nicht mehr nur MyContainer sondern eher Klasse, die beide halten könnte list<int> und list<string>.

Eine Möglichkeit, die Vererbung zu verwenden, wie schon @IanPudney gezeigt hat.

Allerdings können Sie dies auch ohne MyContainer Klassen für Vererbungshierarchie zu schaffen, wenn Sie anstelle der Karte erklären als map<string, boost::any> (siehe http://www.boost.org/doc/libs/1_61_0/doc/html/boost/any.html), dass die Art und Weise Sie ohne die Notwendigkeit, jede Art von MyContainer innerhalb dieser Karte zu speichern, werden in der Lage Vererbungshierarchie erstellen im Voraus.

+0

Vielen Dank für das Teilen dieser Idee. Ich würde lieber nicht boost lib (oder irgendeine andere Third Party Lib) verwenden. – Matthias

0

Es gibt zwei Möglichkeiten, dies zu tun, ohne Vererbung zu verwenden, aber dies kann auch von einigen entscheidenden Faktoren abhängen. Die erste Frage lautet: Beim Erstellen einer Instanz der externen Klasse ist der Typ des Vorlagencontainers bekannt. Wenn es dann ist, ist dies eine einfache Klasse zu schreiben. Wenn es nicht so ist, kann es immer noch getan werden, aber es würde mehr Arbeit involviert sein. Falls der Fall nicht bekannt ist, gibt es 2 Möglichkeiten, dies zu tun, und der erste ist der, den Sie vermeiden wollen, indem Sie dies nicht zu einer Klassenvorlage machen. Die zweite beinhaltet mehr Arbeit, auf der ein Teil der Logik zur Definition dieser Klasse gezeigt wird.

Pseudo-Code: - 1. Fall, in welchem ​​Typ bei der Erstellung bekannt

#include <map> 

template<class T> 
class MyContainer { 
    // ... Class Variables, Constructors & Methods 
} 

// For Demonstration We will say that `T` is known to be an int upon instantiation 
class MyClass { 
private: 
    std::map< std::string, MyContainer<int> > maps_; 

public: 
    MyClass() {} 
    ~MyClass() { 
     // Clear Out Map 
    } 

    void addItem(std::string& str, int value) { 
     maps_.insert(std::make_pair(str, MyContainer<int>(value)); 
    }   
}; 

Jetzt für den zweiten Fall, in dem die Art der Methode ohne Templat den Wrapper nicht bekannt ist, müssen Sie wissen alle Typen, die diese Klasse unterstützen kann, und Sie müssen typedef von jedem von diesen erstellen.

Pseudo-Code - 2. Fall, in dem Typ ist nicht bekannt:

#include <map> 

template<class T> 
class MyContainer { 
    // ... Class Variables, Constructors & Methods 
} 

// For Demonstration We will say that `T` is unknown before instantiation 
class MyClass { 
public: 
    typedef MyContainer<int> INTS; 
    typedef MyContainer<float> FLOATS; 
    typedef MyContainer<double> DOUBLES; 
    // And Do This For Every Type This Class Will Support. 

private: 
    std::map< std::string, INTS > mapInts_; 
    std::map< std::string, FLOATS > mapFloats_; 
    std::map< std::string, DOUBLES > mapDoubles_; 
    // And You Will Need A Container For Each Supporting Type 
public: 

    MyClass() {} 
    // If You Have Constructors Other Than Default That Excepts Parameter Types 
    // You Will Need A Constructor For Each Supporting Type 

    ~MyClass() { 
     // Clear Out All Maps 
    } 

    void addInts(std::string& str, MyClass::INTS); 
    void addFloats(std::string& str, MyClass::FLOATS); 
    void addDoubles(std::string& str, MyClass::DOUBLES); 
    // And You Will Need A Corresponding Function For Each Type This Class Supports. 

}; 
Verwandte Themen