Templatefunktion mit nicht-Template Freund



  • Ich versuche gerade ein einem fremden Code der etwas älter und mir freundlicherweise zur Verfügung gestellt worden ist ein paar Compilerwarnungen zu entfernen, so denn auch diese:

    Warnung: »friend«-Deklaration »std::ostream& operator<<(std::ostream&, const SO<T>&)« deklariert eine Nicht-Template-Funktion

    Der Code sieht wie folgt aus:

    template<class T> class SO {
    
    // ... vieles was nicht beanstandet wird
    
    // der böse Operator:
    friend inline ostream& operator<< (ostream& os, const SO<T>& other);
    };
    

    Soweit so gut, jetzt dachte ich, das müsste einfach nur ein ändern der Zeile in:

    friend inline ostream& operator<< <>(ostream& os, const SO<T>& other);
    

    sein. Was mir auch die unheimliche Verbesserung bringt, dass ich nun keine Warnung mehr habe, sondern einen Fehler:

    Fehler: Deklaration von »operator<<« als Nicht-Funktion

    Autsch! 😮

    Nachgeschlagen bei einem gewissen Herrn Stroustrup - S.370 Kapitel 13.6... der macht es vermeintlich auch nicht anders...
    Aber anscheinend ja doch, oder der GCC machts anders... oder... ich weiß es nicht.

    Kann mir wer sagen, wo mein Fehler liegt?



  • template<class T> class SO {
    
    /* ... */
    
    friend ostream& operator<< (ostream& os, const SO& other);
    };
    
    template <typename T>
    inline ostream& operator << (ostream& os, const T& other)
    {
    /*..*/
    }
    

    allerdings sieht das so aus, als ob du using namespace std; in nem header verwendest?! sollte man niemals machen da man so in allen files in denen man deinen header included den namespace std nutzt - und somit das ganze konzept der namespaces kaputt macht...

    bb



  • Erstmal frohe Weihnachten und vielen Dank für die Hilfe soweit. :xmas1:

    Ja, das mit dem Namespace, da hast du recht.
    Das war vorerst erstmal eine Bequemlichkeitssache, da es sehr sehr viele Fehler beseitigt hat - der Code ist nämlich aus der Zeit vor Namespaces entstanden (ja, auch sowas gibt es noch) und ich war es leid immer wieder std:: zu tippen 😉

    Ich sollte das dringend entfernen, gerade auch weil ich neben der Standardbibliothek auch eine andere Bibliothek (LEDA) verwende, die bspw. auch Strings anbietet und da Probleme entstehen können.

    Ich werde ich deinen Vorschlag demnächst ausprobieren, aktuell bin ich zeitlich etwas kurz angebunden (wie die meisten von uns, nehme ich an).

    Vielen Dank schonmal!



  • Tyrfing schrieb:

    Ich werde ich deinen Vorschlag demnächst ausprobieren, aktuell bin ich zeitlich etwas kurz angebunden (wie die meisten von uns, nehme ich an).

    Hmm... Ich dachte immer, dass es jz ~allen langweilig ist - aber wahrscheinlich ist mein rl einfach zu zurückgebildet ;o)

    bb



  • So, nach einiger Zeit wieder in den Code geschaut... was man nicht alles dringenderes zu erledigen findet, wenn man eine Aufgabe hat, auf die man nicht so recht Lust hat 😉

    Ganz so schlimm war es mit dem Using dann doch nicht, ich habe nicht die std standardmäßig eingebunden sondern "nur" die std::ostream und einige andere Dinge - also bspw. den kritischen String nicht, wobei das habe ich nun überarbeitet.

    Dein Änderungsvorschlag stößt aber auch nicht auf Compilersegen und ich verstehe ihn auch nicht so recht, muss ich sagen.

    Der Compiler sagt mir dazu, dass ich eine nicht-eindeutige Referenz auf den Operator<< hätte und allerlei undurchsichtigen Std-Template-Fehlermeldungen *seufz*
    Nachtrag: Und noch schlimmer, die Fehlermeldungen kommen auch erst, nachdem er festgestellt hat, dass ich immer noch einen "nicht Template Freund" deklariert habe. Also scheint's mir, ich habe das Problem nicht beseitigt und ausserdem einen weiteren Fehler eingebaut.

    Ich sag dazu erstmal folgendes:

    template<class T> class SO {
    
    /* ... */
    
    friend ostream& operator<< (ostream& os, const SO& other);
    };
    
    template <typename T>
    inline ostream& operator << (ostream& os, const T& other)
    {
    /*..*/
    }
    

    Oben in der Klasse wird eine Funktion definiert, die als zweiten Parameter eine Instanz der mit T parametrisierten Klasse SO erwartet. Bei der Implementierung hingegen erwarten wir als zweiten Parameter nicht mehr eine Instanz der Klasse SO sondern eine den Typen T?!

    Also um es konkreter zu machen, wenn es um SO<int> ginge, dann würde ich nun bei der Ausgabe nicht mehr die Klasse SO<int> erwarten, sondern direkt ein int?
    Klingt für mich erstmal nicht einleuchtend...

    Nachtrag:
    ---------

    Also, was meines erachtens eigentlich funktionieren müsste, aber es nicht tut ist der folgende Code:

    template<class T> class SO {
      /* ... */
      friend std::ostream& operator<< <>  (std::ostream&, const SO<T>&);
    
    private:
      T* obj_;
    };
    

    und dann etwas später:

    template<class T> std::ostream& operator<< (std::ostream& os,
                   const SO<T>& other) {
      os << other.obj_;
      return (os);
    }
    

    Was mir aber mit einem:

    Fehler: Deklaration von »operator<<« als Nicht-Funktion
    Fehler: expected »;« before »<« token

    vom Compiler quitiert wird?



  • Hi,

    die Lösung für dein ursprüngliches Problem sollte sich durch Forward-Deklarationen lösen lassen:

    template<class T> class SO;
    template<class T>
    std::ostream& operator<<(std::ostream&, const SO<T>&);
    
    template<class T> class SO
    {
      ...
      friend std::ostream& operator<< <>(std::ostream&,const SO<T>& );
    };
    
    //Definition
    ...
    


  • Der Orginalcode ist eigentlich richtig. Ich bekomm auch keine Warnung. Vielleicht wirklich ne gcc Macke??



  • Welchen Compiler nutzt du?

    Der gcc 4.3 (MinGW-Portierung) warnt auf jeden Fall.



  • Visual C++ 2005 und Comeau.



  • Tatsächlich, sehr interessant. Die beiden lassen es durch 😉



  • Okay, ich glaube das Problem ist nun gelöst, vielen Dank an euch drei und natürlich insbesondere Aquae für den ausschlaggebenden Tipp.

    Damit andere (oder ich selbst, wenn ich nochmal drüber falle) es auch nochmal schön nachlesen können, was geholfen hat, hier dann die korrigierte Version:

    // Forward Deklaration
    template<class T> class SO;
    template<class T>  std::ostream& operator<< (std::ostream&, const SO<T>&);
    
    /* ... ... ...*/
    
    // eigentliche Klasse mit problematischem Freund
    template<class T> class SO {
      /* ... */
      friend std::ostream& operator<< <>(std::ostream& os, const SO<T>& other);
    private:
      T* obj_;
    };
    
    /* ... ... ... */
    
    // Implementation des Freundes
    template<class T> inline
    std::ostream& operator<< (std::ostream& os,const SO<T>& other) {
      os << other.obj_;
      return (os);
    }
    

    Für mich dabei interessant ist, wo an welcher Stelle ich bitteschön ein "<>" einfügen musste und wo es überall nicht okay war.
    Zum Beispiel ganz unten in der Implementation war es nicht okay eins einzufügen, hingegen bei der Friend-Deklaration musste es stehen, sonst gab's die Warnung, die ich ursprünglich entfernen wollte.

    Nochmals, vielen Dank!
    Dann mach ich mich jetzt mal auf die Socken das an diversen Stellen des Quellcodes zu überarbeiten *seufz*

    Nachtrag: Jetzt ist mir doch tatsächlich das inline dabei verloren gegangen, nachgetragen.



  • Das <> muss in die friend-Deklaration rein, damit der Compiler weiss, dass er die Template-Version von operator<< benutzen soll. Er sieht ja bei Inspektion der Klasse nicht

    friend std::ostream& operator<<(std::ostream& os, const SO<T>& other); //falls ohne <>
    

    sondern schon den erzeugte Funktion

    friend std::ostream& operator<<(std::ostream& os, const SO<int>& other); //Sei T = int
    

    welches für ihn eine Nicht-Template-Funktion ist.

    Mir ist jetzt nur rätselhaft, dass es tatsächlich nur mit einer Warning compiliert und gelinkt wird... schliesslich findet er doch diese Nicht-Template-Variante nirgendwo?


Anmelden zum Antworten