Templates: undefined reference (g++-3.3 vs. g++-4.1) [gelöst]



  • template<> WorkerClass  Singleton<MyClass>::m_Worker;
    

    Das hier ist leider keine Definition sondern eine sogenannte nondefining declaration. Die ist für normale statische Klassenmember nicht erlaubt, bei voll spezialisierten statischen Membern von Klassentemplates aber schon. Das hat dummerweise zur Folge, dass statische Member von Klassentemplates, die nur einen default-Ctor haben, nicht vollständig spezialisiert werden können, wie du es hier machst.
    Andererseits kann man ja immer die allgemeine default-initialisierte Definition mit dem Template Header mitliefern und bei Bedarf die Spezialisierung da anbringen, wo die default-Initialisierung nicht adäquat ist.

    Lösung daher:

    // H-FILE:  TEMPLATE FOR SINGLEION
    template<class TYPE> class Singleton
    {
    public:
        static void DummyFunc() { M_pObject = new TYPE();   m_Worker.DoWork();  };
    
    private:
        static WorkerClass m_Worker;
        static TYPE* M_pObject;
    };
    
    template <class TYPE> WorkerClass Singleton<TYPE>::m_Worker;
    template <class TYPE> Type* Singleton<TYPE>::M_pObject = 0;
    

    PS: dir ist klar dass du in DummyFunc() so jedesmal ein Speicherleck erzeugst? Die TYPEs werden nie zerstört...



  • pumuckl, fehlen da nicht noch die Rückgabetypen?

    template <class TYPE> WorkerClass Singleton<TYPE>::m_Worker;
    template <class TYPE> Type* Singleton<TYPE>::M_pObject = 0;
    


  • Ja, danke für den Hinweis, habs reineditiert.


  • Mod

    template<> WorkerClass Singleton<MyClass>::m_Worker = WorkerClass();
    


  • SUPER - DANKE

    So funktioniert es jetzt:

    template <class TYPE> WorkerClass Singleton<TYPE>::m_Worker;
    template<> MyClass* Singleton<MyClass>::M_pObject = NULL;
    

    Mein Problem war wohl, das ich nur alle paar Jahre Templates benutze.
    Und wenn ich welche benutze, dann schaue ich immer nach wie ich es früher gemacht habe.
    Gleichzeitig mit C++0x werde ich mir auch noch mal Templates anschauen müssen.

    In der zweiten Zeile verhält es sich wohl so wie bei integrierten Datentypen (e.g. int usw.), da ist immer noch meine alte Schreibweise OK.

    Nochmals wirklich vielen Dank
    Mauze



  • SUPER - DANKE

    So funktioniert es jetzt:

    template <class TYPE> WorkerClass Singleton<TYPE>::m_Worker;
    template<> MyClass* Singleton<MyClass>::M_pObject = NULL;
    

    Mein Problem war wohl, das ich nur alle paar Jahre Templates benutze.
    Und wenn ich welche benutze, dann schaue ich immer nach wie ich es früher gemacht habe.
    Gleichzeitig mit C++0x werde ich mir auch noch mal Templates anschauen müssen.

    In der zweiten Zeile verhält es sich wohl so wie bei integrierten Datentypen (e.g. int usw.), da ist immer noch meine alte Schreibweise OK.

    Nochmals wirklich vielen Dank
    Mauze



  • Mauze schrieb:

    In der zweiten Zeile verhält es sich wohl so wie bei integrierten Datentypen (e.g. int usw.), da ist immer noch meine alte Schreibweise OK.

    Jein. Das zweite funktioniert weil der Pointer nicht default-initialisiert wird.

    Die beiden Definitionen gehören außerdem in zwei verschiedene Code-Teile und haben unterschiedliche Auswirkungen:
    Das Template mit der Definition von m_Worker gehört in den Header mit dem Klassentemplate und sorgt dafür, das jede Instantiierung des Templates automatisch die entsprechende Definition des Members hat.
    Die Spezialisierung der Definition von m_pObject gehört dagegen in den Teil, wo die entsprechende Spezialisierung von Sigleton benutzt wird (also in die .cpp), weil sie nur für die eine Instantiierung gilt. Außerdem muss die gleiche Definition für jede weitere Instantiierung von Singleton wiederholt werden.
    Ich würde aus deinem Code heraus da keinen Grund für sehen und lieber auch gleich die Definition als template für alle Instantiierungen von Singleton mit in dessen Header packen.



  • Das ist jetzt der komplette fertige Code.

    Zu beachten ist, das die entsprechenden Codeanteile in Wirklichkeit auch in den entsprechenden Dateien aufgeteilt werden.
    Warum sage ich das, weil ich bei der Übernahme des Codes in mein wirkliches Projekt immer noch nicht linken konnte (weil aufgeteilt auf LIB, SO und EXE).

    Das Jein von Pumuckl mit der entsprechenden Erklärung hat mich dann in die Lage versetzt, mein Projekt endlich mit g++-4.3 linken zu können.

    DANKE - DANKE - DANKE

    #include <cstdio>
    
    // H-FILE: WORKER CLASS
    class WorkerClass
    {
    public:
        void DoWork( void ) {;}
    };
    
    // H-FILE:  TEMPLATE FOR SINGLEION
    template<class TYPE> class Singleton
    {
    public:
        static void DummyFunc() { M_pObject = new TYPE();   m_Worker.DoWork(); delete M_pObject; };
    
    private:
        static WorkerClass m_Worker;
        static TYPE* M_pObject;
    };
    template <class TYPE> WorkerClass Singleton<TYPE>::m_Worker;
    
    // H-FILE: MY CLASS
    class MyClass : public Singleton<MyClass> {
    public:
        friend class Singleton<MyClass>;
        WorkerClass  m_CritSec;
    };
    
    //////////////
    // CPP-FILE //
    //////////////
    
    template<> MyClass* Singleton<MyClass>::M_pObject = NULL;
    
    int main( int argc, char** argv)
    {
        MyClass::DummyFunc();
    }
    

    Ich denke jetzt aber wirklich gelöst - ODER?

    Mauze



  • Mauze schrieb:

    Ich denke jetzt aber wirklich gelöst - ODER?

    Immernoch jein. Wie oben schon gesagt musst du so immernoch für jede neue Intantiierung des Templates eine neue vollkommen spezialisierte Definition von M_pObject dazu packen, um dafür nicht undefined references zu kriegen.

    Also nochmal die Frage: Was hindert dich daran, auch M_pObject im Header so zu definieren wie vorgeschlagen, nämlich per Template? Die Initialisierung mit NULL ist für alle Instantiierungen so doch völlig in Ordnung.



  • Das Forum war irgendwie nicht mehr erreichbar, darum jetzt erst eine Antwort.

    Wenn ich das so wie von Dir vorgeschlagen versuchte, dann bekam ich immer folgenden Compiler Fehler:

    error: expected constructor, destructor, or type conversion before ‘*’ token
    

    Dann ist mir endlich der Tippfehler aufgefallen:

    [b]FALSCH:[/b]  template <class TYPE> [b]Type*[/b] Singleton<TYPE>::M_pObject = 0;
    [b]RICHTIG:[/b] template <class TYPE> [b]TYPE*[/b] Singleton<TYPE>::M_pObject = 0;
    

    Das ist jetzt der Code ohne JEIN:

    #include <cstdio>
    
    // H-FILE: WORKER CLASS
    class WorkerClass
    {
    public:
        void DoWork( void ) {;}
    };
    
    // H-FILE:  TEMPLATE FOR SINGLEION
    template<class TYPE> class Singleton
    {
    public:
        static void DummyFunc() { M_pObject = new TYPE();   m_Worker.DoWork(); delete M_pObject; };
    
    private:
        static WorkerClass m_Worker;
        static TYPE* M_pObject;
    };
    template <class TYPE> WorkerClass Singleton<TYPE>::m_Worker;
    template <class TYPE> TYPE* Singleton<TYPE>::M_pObject = 0;
    
    // H-FILE: MY CLASS
    class MyClass : public Singleton<MyClass> {
    public:
        friend class Singleton<MyClass>;
    };
    
    //////////////
    // CPP-FILE //
    //////////////
    
    int main( int argc, char** argv)
    {
        MyClass::DummyFunc();
    }
    

    Mauze



  • Mauze schrieb:

    Das ist jetzt der Code ohne JEIN:

    Genau 🙂


Anmelden zum Antworten