too few template-arguments-lists



  • Hallo,
    ich habe folgende Singleton-Objekte:

    // Datei A.hpp
    template <class T>
    class A{
    private:
       static A<T>* Instance;
    
       // Ctor und Copy-Ctor
    public:
       static A<T>* getInstance(){
           if(!Instance){
              Instance = new T();
              return Instance;
           }
           return Instance;
       }
    protected:
       // Irgendwelche Schnittstellendefinitionen 
    };
    
    // Datei B.hpp
    class B : public A<B>{
    friend class A<B>;
    private:
       // Ctor und Copy-Ctor
    public:
       // Schnittstellimplementierungen
    };
    A<B>* A<B>::Instance = NULL;
    

    Kompiliere ich obiges Konstukt mit dem MSVC in der 2008er Version, erhalte ich keine Fehler. Selbiges wird aber unter NetBeans mit dem g++-Compiler (Müsste die neuste Version 4.4 sein) in Zeile 28 immer mit der Fehlermeldung: "Too few template-arguments-lists" quittiert. Was mache ich hier falsch?



  • Wenn ich das richtig sehe ist das in Zeile 28 eine vollständige Spezialisierung von A<B>::Instance. Evtl. wird der Fehler behoben wenn du ein template<> vor die Zeile schreibst.
    Dann wird allerdings das nächste problem auftauchen: sobald du B.hpp in mehr als eine Übersetzungseinheit einbindest, wird der Linker am Ende über eine "multiple definition" meckern. Die Definition gehört in eine .cpp
    Allgemein wirst du für alle anderen Instantiierungen von A aber dann noch den Fehler erhalten "undefined reference to A<C>::Instance" - weil du Instance eben nur für B definiert hast und für alle anderen möglichen Templateparameter nicht. Um das zu beheben könntest du einfach allgemein A<T>::Instance = NULL in A.hpp schreiben (hier in die hpp, weils ein Template und keine vollständige Spezialisierung ist). Die Spezialisierung für B kann dann entfallen.



  • pumuckl schrieb:

    Dann wird allerdings das nächste problem auftauchen: sobald du B.hpp in mehr als eine Übersetzungseinheit einbindest, wird der Linker am Ende über eine "multiple definition" meckern. Die Definition gehört in eine .cpp

    B.hpp wird im Code nur einmal an einer zentralen Stelle eingebunden.

    pumuckl schrieb:

    Allgemein wirst du für alle anderen Instantiierungen von A aber dann noch den Fehler erhalten "undefined reference to A<C>::Instance" - weil du Instance eben nur für B definiert hast und für alle anderen möglichen Templateparameter nicht

    Jede abgeleitete Klasse von A<T> entspricht einer betriebssystemspezifischen Implementierung eines globalen "EventListeners".(ich schreibe momentan an einer kleinen GUI-Lib). Es gibt momentan nur zwei solcher Ableitungen: Einmal für Win32-Systeme und eine für Linux. Es ist also nicht geplant, noch weitere Instanttierungen vorzunehmen, solange es nicht notwendig ist.

    Ich werde es erstmal mit vorangestelltem template<> verursachen und evt. dann noch die restlichen Punkte abarbeiten 😉 Vielen Dank für die schnelle Antwort



  • Wäre es nicht schöner, wenn es

    static T* Instance;
    
       static T* getInstance(){
           if(!Instance){
              Instance = new T();
           }
           return Instance;
       }
    

    heißen würde?



  • Michael E. schrieb:

    Wäre es nicht schöner, wenn ...

    Damit könntest du Recht haben 😉 Hab den Code entsprechend angepasst.



  • pumuckl schrieb:

    Allgemein wirst du für alle anderen Instantiierungen von A aber dann noch den Fehler erhalten "undefined reference to A<C>::Instance" - weil du Instance eben nur für B definiert hast und für alle anderen möglichen Templateparameter nicht. Um das zu beheben könntest du einfach allgemein A<T>::Instance = NULL in A.hpp schreiben (hier in die hpp, weils ein Template und keine vollständige Spezialisierung ist). Die Spezialisierung für B kann dann entfallen.

    Ich habe diesen Vorschlag nun ausprobiert, da er meiner Meinung nach die sinnvollste und sauberste Lösung darstellt:

    // Datei A.hpp
    template <class T>
    class A{
    private:
       static A<T>* Instance;
    
       // Ctor und Copy-Ctor
    public:
       static A<T>* getInstance(){
           if(!Instance){
              Instance = new T();
              return Instance;
           }
           return Instance;
       }
    protected:
       // Irgendwelche Schnittstellendefinitionen
    };
    A<T>::Instance = NULL;
    

    Aber anschließend bekomme ich immer wieder die Fehlermeldung, dass "T" in diesem Gültigkeitsbereich nicht definiert wurde(mit Verweis auf die Zeile 19). Wie nehme ich nun die Initialisierung für Instance vor, ohne das Template spezialisieren zu müssen?


  • Administrator

    Wie wäre es mit:

    template<typename T>
    A<T>* A<T>::Instance = NULL;
    

    Aber sag mal, bist du sicher, dass es eine A<T> Instanz sein soll und nicht gleich eine T Instanz?

    Und wieso machst du dir das Leben nicht einfacher und machst sowas:

    // Datei A.hpp
    template <class T>
    class A{
       // Ctor und Copy-Ctor
    public:
       static A<T>* getInstance(){
           static A<T>* Instance = new T();
           return Instance;
       }
    protected:
       // Irgendwelche Schnittstellendefinitionen
    };
    

    Falls das Objekt nicht zu gross ist, könntest du es sogar direkt bauen lassen und nicht über den Heap gehen:

    static A<T>& getInstance(){
       static T Instance;
       return Instance;
    }
    

    Grüssli



  • Hi,

    Dravere schrieb:

    Wie wäre es mit:

    template<typename T>
    A<T>* A<T>::Instance = NULL;
    

    Jupp, funktioniert, danke. Und ich hatte mich tatsächlich die ganze Zeit gefragt, warum T undefiniert ist 😃
    Hier würde es tatsächlich auch reichen, nur eine Instanz vom Typ T zurückzugeben. Habs entsprechend geändert.

    Dravere schrieb:

    Und wieso machst du dir das Leben nicht einfacher und machst sowas:

    // Datei A.hpp
    template <class T>
    class A{
       // Ctor und Copy-Ctor
    public:
       static A<T>* getInstance(){
           static A<T>* Instance = new T();
           return Instance;
       }
    protected:
       // Irgendwelche Schnittstellendefinitionen
    };
    

    Hmm, wenn ich an dieser Stelle getInstance() zweimal aufrufe, was passiert dann mit dem ersten - auf dem Heap angelegten - T? Ich könnte dann doch theoretisch folgendes machen:

    typedef A<T> Singleton;
    static Singleton* foo = Singleton::getInstance();
    static Singleton* bar = Singleton::getInstance();
    // 2 verschiedene Instanzen von Singleton
    

    Vielleicht ist es aber schon zu spät, und ich raffe es nicht 😉



  • Nein, statische Initialisierungen passieren nur einmal pro Programmdurchlauf.

    Aber wieso auf dem Heap? Wer soll das dann freigeben? Ausserdem würde ich keinen Zeiger, sondern eine Referenz zurückgeben.



  • Nexus schrieb:

    Nein, statische Initialisierungen passieren nur einmal pro Programmdurchlauf.

    Okay, ich war gerade in dem Glauben, dass eine neue statische Variable innerhalb der Funktion generiert wird, was sich jetzt nach mehrmaliger Überlegung natürlich als falsch herausstellt 😃

    Nexus schrieb:

    Aber wieso auf dem Heap? Wer soll das dann freigeben? Ausserdem würde ich keinen Zeiger, sondern eine Referenz zurückgeben.

    In meinem Quellcode versteckt sich noch eine kleine statische Memberfunktion destroy(), die den Speicher am Ende des Programms wieder freigibt. 😉



  • Matzer schrieb:

    In meinem Quellcode versteckt sich noch eine kleine statische Memberfunktion destroy(), die den Speicher am Ende des Programms wieder freigibt. 😉

    Sehr heikel, sowas geht extrem schnell vergessen. Gerade wenn das Programm frühzeitig beendet wird und das Projekt wächst. Wieso nicht direkt im statischen Speicherbereich arbeiten oder wenigstens den Zeiger kapseln?

    Ach ja, Referenzen fände ich immer noch besser. 😉
    Hat es einen bestimmten Grund, wieso du Zeiger nimmst? Wenn du dauernd dereferenzierst, kann eine Referenz schöner sein. Aber ist auch ein wenig Geschmackssache... 🙂



  • Nexus schrieb:

    Matzer schrieb:
    In meinem Quellcode versteckt sich noch eine kleine statische Memberfunktion destroy(), die den Speicher am Ende des Programms wieder freigibt. 😉
    Sehr heikel, sowas geht extrem schnell vergessen. Gerade wenn das Programm frühzeitig beendet wird und das Projekt wächst. Wieso nicht direkt im statischen Speicherbereich arbeiten oder wenigstens den Zeiger kapseln?

    Momentan schreibe ich an einer GUI-Lib und um boost::asio dazu zu überreden, einen asynchronen Aufruf zu meinem globalen EventHandler zu starten, muss am Ende jedes Programms ungefähr sowas stehen:

    // irgendwo im Code
    boost::asio::io_service MsgHandler;
    //in main()
    MsgHandler.run();
    return 0;
    

    Natürlich ist der Aufruf von run in irgendeiner Klasse gekapselt. Da aber run() am Ende des Programms sowieso ausgeführt werden muss, übernimmt die kapselnde Klasse anschließend auch die Aufräumarbeiten, sobald die globale MessageLoop durchlaufen ist. Vergessen wird es also erstmal nicht 😉

    Nexus schrieb:

    ...oder wenigstens den Zeiger kapseln?

    kommt alles noch 🙂 Ich wollte es erstmal schaffen, auf Linux und Windows je ein Fenster zur Anzeige zu bringen (was mir vorhin auch gelungen ist muhaa). Deswegen wird in einigen Teilen des Codes noch mir rohen Zeigern rumhantiert. Ich wollte nur so schnell wie möglich zum Ziel. Das geht natürlich auf Kosten des Designs

    Nexus schrieb:

    Ach ja, Referenzen fände ich immer noch besser. 😉
    Hat es einen bestimmten Grund, wieso du Zeiger nimmst? Wenn du dauernd dereferenzierst, kann eine Referenz schöner sein. Aber ist auch ein wenig Geschmackssache... 🙂

    Der hartnäckigen Forderung nach einer Referenz werde ich natürlich nachkommen 😉 Ist an dieser Stelle wohl tatsächlich angebrachter.



  • Würd dir empfehlen, dieses buch zu holen:

    http://www.amazon.com/Modern-Design-Programming-Patterns-Depth/dp/0201704315

    Da gibt es ein größeres beispiel zu einer modernen gui lib, afaik. Falls es mal irgendwo hapert.


Anmelden zum Antworten