Template mit friend liefert "unresolved external symbol" error



  • Hi

    Ich habe vor eine eigene Vektorklasse zu schreiben, da sich diese gut eignet um es mit Function Overloading zu spielen. Dabei moechte ich die Klasse als Template implementieren.

    Ich habe nun folgenden Code (in einem Header-File):

    #include <iostream>
    
    template<typename T>
    class Vector
    {
    private:
    	T x;
    	T y;
    
    public:
    	Vector( T a_x, T a_y ) : x(a_x), y(a_y) {}
    
    	// Overloaded operators
    	friend std::ostream & operator<< ( const std::ostream & os, const Vector<T> & v );
    };
    
    // Overloaded operators
    template<typename T>
    std::ostream & operator<< ( const std::ostream & os, const Vector<T> & v )
    {
    	os << "(" << v.x << "," << v.y << ")";
    	return os;
    }
    

    Ich bekomme allerdings folgenden Fehler (MSVS 2010):

    Error 1 error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > const &,class Vector<int> const &)" (??6@YAAAV?basic_ostream@DU?basic\_ostream@DU?char_traits@D@std@@@std@@ABV01@ABV?$Vector@H@@@Z) referenced in function _main

    Ich nehme mal an, dass es es einen Fehler bim Linken gibt. Ich habe vorher gerade einen Artikel gelesen wie mit Templates zur Compile-Time Code generiert und gelinkt wird aber irgendwie verstehe ich das ganze noch nicht so genau.

    Kann mir da jemand helfen?


  • Mod

    Zwei Fehler, eine Kleinigkeit und das wonach du gefragt hast:
    Die Kleinigkeit zuerst: Du kannst nix in eine const ostream& schreiben und aus einer const ostream& bei der Rückgabe keine ostream& machen. Das habe ich im folgenden stillschweigend korrigiert.

    Das wonach du fragst:
    1. Warnoptionen hochdrehen. Immer. Mein Compiler sagte zum Beispiel:

    test.cc:14:85: warning: friend declaration ‘std::ostream& operator<<(const ostream&, const Vector<T>&)’ declares a non-template function [-Wnon-template-friend]
    test.cc:14:85: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
    

    2. Und so hat der Compiler schon komplett erklärt, wie es richtig geht:

    #include <iostream>
    
    // "make sure the function template has already been declared"
    template<typename T> class Vector;
    template<typename T> std::ostream & operator<< (std::ostream &, const Vector<T> &);
    // Ok, das haben wir hiermit gemacht.
    
    template<typename T>
    class Vector
    {
    private:
        T x;
        T y;
    
    public:
        Vector( T a_x, T a_y ) : x(a_x), y(a_y) {}
    
        // "and add <> after the function name here"
        friend std::ostream & operator<< <>(std::ostream & os, const Vector<T> & v );
        // War auch nicht schwer
    };
    
    // Und schon funktioniert's
    template<typename T>
    std::ostream & operator<< (std::ostream & os, const Vector<T> & v )
    {
        os << "(" << v.x << "," << v.y << ")";
        return os;
    }
    

    Alternativ, mit viel weniger Verrenkungen, kannst du die friend-Funktionen auch direkt in der Klasse definieren (es sind dann trotzdem globale Funktionenedit: Genauer: Sie gehören zum Namespace der umgebenden Klasse und können nur mittels ADL gefunden werden.):

    #include <iostream>
    
    template<typename T>
    class Vector
    {
    private:
      T x;
      T y;
    
    public:
      Vector( T a_x, T a_y ) : x(a_x), y(a_y) {}
    
      // ist doch viel hübscher so
      friend std::ostream & operator<<(std::ostream & os, const Vector<T> & v )
      {
        os << "(" << v.x << "," << v.y << ")";
        return os;
      } 
    };
    


  • Danke schonmal soweit.

    Zu 1.
    Ich werde mal googeln und sehen wie ich in MSVS die Warnoptionen hochdrehen kann.

    Zu 2.
    Wozu braucht es das <> bei

    friend std::ostream & operator<< <>(std::ostream & os, const Vector<T> & v );
    

    Ich bin nicht mehr so fit mit Templates. Ich glaube ich muss da ein par Dinge nochmals nachlesen.

    Zur Alternative:
    Jo, ist recht huebsch so. Werde ich wohl so machen 🙂


  • Mod

    icarus2 schrieb:

    Zu 2.
    Wozu braucht es das <> bei

    friend std::ostream & operator<< <>(std::ostream & os, const Vector<T> & v );
    

    Ich bin nicht mehr so fit mit Templates. Ich glaube ich muss da ein par Dinge nochmals nachlesen.

    Na, das was mein Compiler da angemeckert hat:
    Wenn du die friend-Funktion in der Klasse so deklarierst, wie du es ursprünglich hattest, dann ist sie kein Funktionstemplate, sondern du deklarierst eine Funktion mit eben jenem Parameter, mit dem das Template instanziert wurde. Und das passt dann später nicht zu der Definition, die du als Template gemacht hast.

    Daher muss entweder die friend-Deklaration selber auf ein Funktionstemplate verweisen. Das ist die 1. Variante und dies ist nun einmal die Syntax, wenn du den Templateparameter wiederverwenden möchtest. Du könntest auch noch einen weiteren Templateparameter innerhalb der Klasse einführen und stattdessen template<class T2> friend std::ostream & operator<<(std::ostream & os, const Vector<T2> & v ); schreiben. Dann sind aber alle Funktionen dieser Art friends deiner speziellen Templateinstanzierung, nicht nur das spezielle mit dem gleichen Parameter. Daher willst du sicherlich das erstere, da es restriktiver ist.

    Oder du wählst die zweite Variante und deine friend-Funktion ist kein Template mehr, aber wird dann eben instanziert, wenn das umgebende Klassentemplate instanziert wird. Und da man die Funktion mit einem Objekt der Klasse als Parameter aufruft, wird sie dann dank argument dependend look-up auch gefunden. Folgendes funktioniert beispielsweise nicht:

    class Foo{};
    
    template<typename T> class Vector
    {
      friend std::ostream & operator<<(std::ostream & os, const T &v ) {return os;}
    };
    
    int main()
    {
      Foo t;
      Vector<Foo> u;
      std::cout << t << '\n';  // Finde den Operator nicht, da ADL nicht greift
    }
    


  • Vielen Dank fuer die ausfuehrliche Erklaerung.


Anmelden zum Antworten