Zusammenhang Templates und "undefined reference"



  • Hallo zusammen,

    ich versuche eine relativ einfache klasse zu erstellen, die einen 2D-vector von beliebigem Typ zur verfügung stellt. ich poste erstmal den code (aufs wesentliche reduziert)

    //main.cpp
    #include "field.hpp"
    int main()
    {
        Field<int> field(10, 10);
    
    	return 0;
    }
    
    //field.hpp
    #ifndef FIELD_HPP_INCLUDED
    #define FIELD_HPP_INCLUDED
    #include <vector>
    
    template<typename T>
    class Field
    {
    	public:
    
    	int height, width;
    	std::vector< std::vector<T> > field;
    
    	std::vector<T>& operator[](int i);
    	T &at(int x, int y);
    
    	Field(int theight, int twidth);
    	~Field();
    };
    #endif
    
    //field.cpp
    #include <vector>
    #include "field.hpp"
    
    template<typename T>
    Field<T>::Field(int twidth, int theight)
    {
        height = theight;
        width = twidth;
        field.resize(twidth, std::vector<T>(theight));
    }
    
    template<typename T>
    Field<T>::~Field() {}
    
    template<typename T>
    std::vector<T> &Field<T>::operator[](int i)
    {
    	return field[i];
    }
    
    template<typename T>
    T &Field<T>::at(int x, int y)
    {
    	x = (x >= width ? x - width : (x < 0 ? x + width : x) );
    	y = (y >= height ? y - height : (y < 0 ? y + height : y) );
    
    	return field[x][y];
    }
    

    Wenn ich den Code Kompiliere über

    g++ main.cpp field.cpp
    

    bekomme ich die Meldung

    /tmp/ccQXnkJN.o: In function `main':
    main.cpp:(.text+0x22): undefined reference to `Field<int>::Field(int, int)'
    main.cpp:(.text+0x33): undefined reference to `Field<int>::~Field()'
    collect2: ld returned 1 exit status
    

    Das Kompilieren (ohne linken) an sich läuft erfolgreich, d.h.

    g++ main.cpp field.cpp -c
    

    liefert keinen Fehler, wenn ich die Templates deaktiviere, also den Datentyp vorher auf z.B. int festlege, bekomme ich auch keine Meldung, so aber schon.

    ich hoffe irgendjemand weiß da einen rat.
    Danke!



  • Einen dicken Daumen nach oben für Die Nutzung von std::vector. 👍

    n0xinger schrieb:

    //field.hpp
    #ifndef FIELD_HPP_INCLUDED
    #define FIELD_HPP_INCLUDED
    #include <vector>
    
    template<typename T>
    class Field
    {
    public:
    
    	int height, width;
    	std::vector< std::vector<T> > field;
    
    	std::vector<T>& operator[](int i);
    	T &at(int x, int y);
    
    	Field(int theight, int twidth);
    	~Field();
    };
    #endif
    

    Einen eigenen Destruktor brauchst Du nicht. Der, der vom Compiler implizit erzeugt werden würde, macht genau das richtige.

    Das Problem ist, dass Du glaubst, man könne die Implementierungen von Funktions-Templates oder Elementfunktionen von Klassen-Templates in CPP-Dateien "verstecken". Dem ist nicht so. Wenn Du Dein field.cpp kompilierst, wird sich der Compiler den Quellcode anschauen, nichts übersetzen, und wieder vergessen. Wenn der Compiler main.cpp übersetzt, sieht er die Definition des Klassentemplates inklusive der Funktionsdeklarationen. In main.cpp machst Du Gebrauch von Field<int>::Field und Field<int>::~Field. Der Compiler kann den Maschinencode für diese Funktionen aber nicht generieren, weil er nix von field.cpp weiß. Deswegen meckert der Linker auch. main.cpp braucht Funktionen, die nicht generiert worden sind.

    Stattdessen packst Du die Funktionen mit in den Header rein. Das ist erlaubt. Die ODR (one definition rule) macht für "Funktionen, die mit Tempaltes zusammenhängen" eine Ausnahme.


Log in to reply