Templateimplementierung, Fehler mit .cpp-Datei[ GELÖST ]



  • Hey Leute,
    ich hab eine Frage bezüglich eines Verhaltens des Compilers was ich nicht so ganz verstehe.
    Wenn ich jetzt mit dem Modell der einschließenden Kompilierung ein Template habe, also die Implementierung in einer Quelldatei also .cpp dann erhalte ich immer komische Compilerfehler, wie ein ";" wurde vor "<" erwartet.
    Habe im Internet gelesen man soll eine .txx datei nehmen, jedoch ohne Begründung, habe das dann gemacht, was auch funktioniert. Mich würde aber interessieren, wieso es mit einer .txx Datei geht und mit einer .cpp Datei nicht geht und was vor allem eine .txx Datei ist und wie ich das ganze mit einer .cpp Datei zum laufen bekomme.

    Danke schonmal für die Hilfe.

    Lg freeG



  • Zeig doch mal den Code. Falls in deinem Templte << steht, mach ein Leerzeichen dazwischen < <



  • Also hier mal als Beispiel ein Code bei dem es vorkommt:

    Template.h

    #ifndef TEMPLATES2_H
    #define TEMPLATES2_H
    
    #include <iostream>
    
    template< class Type > std::ostream& operator<<( std::ostream&, const Queue< Type >& );
    template< class Type > std::istream& operator>>( std::istream&, Queue< Type >& );
    
    template< class Type > class Queue
    {
    	public:
    		friend std::ostream& operator<< < Type > ( std::ostream&,  const Queue< Type >& );
    		friend std::istream& operator>> < Type > ( std::istream&, Queue< Type >& );
    		Queue();
    		template< class Iter >Queue( Iter, Iter );
    		Queue( const Queue& );
    		Queue& operator=( const Queue& );
    		~Queue();
    		Type& front();
    		const Type& front() const;
    		void push( const Type& );
    		void pop();
    		bool empty() const;
    		template< class Iter > void assign( Iter, Iter );
    
    	private:
    		QueueItem< Type >* head;
    		QueueItem< Type >* tail;
    		void destroy();
    		void copy_elems( const Queue& );
    		template< class Iter > void copy_elems( Iter, Iter );
    };
    
    #include "Templates2.txx"
    
    #endif
    

    Template.cpp

    #include <iostream>
    
    using std::ostream;
    using std::istream;
    
    template< class Type >
    Queue< Type >::Queue():
    	head( 0 ), tail( 0 )
    	{
    	}
    
    template< class Type > template< class Iter >
    Queue< Type >::Queue( Iter beg, Iter end ):
    	head( 0 ), tail( 0 )
    	{
    		copy_elems( beg, end );
    	}
    
    template< class Type >
    Queue< Type >::Queue( const Queue& orig ):
    	head( 0 ), tail( 0 )
    	{
    		copy_elems( orig );
    	}
    
    template< class Type >
    Queue< Type >::~Queue()
    {
    	destroy();
    }
    
    template< class Type >
    Queue< Type >& Queue< Type >::operator=( const Queue< Type >& rhs )
    {
    	if( this != &rhs )
    	{
    		destroy();
    		copy_elems( rhs );
    	}
    	return *this;
    }
    
    template< class Type >
    Type& Queue< Type >::front()
    {
    	return head->item;
    }
    
    template< class Type >
    const Type& Queue< Type >::front() const
    {
    	return head->item;
    }
    
    template< class Type >
    void Queue< Type >::push( const Type& t )
    {
    	QueueItem< Type >* pt = new QueueItem< Type >( t );
    	if( empty() )
    		head = tail = pt;
    	else
    	{
    		tail->next = pt;
    		tail = pt;
    	}
    }
    
    template< class Type >
    void Queue< Type >::pop()
    {
    	QueueItem< Type >* p = head;
    	head = head->next;
    	delete p;
    }
    
    template< class Type >
    bool Queue< Type >::empty() const
    {
    	return head == 0;
    }
    
    template< class Type >
    void Queue< Type >::destroy()
    {
    	while( !empty() )
    		pop();
    }
    
    template< class Type >
    void Queue< Type >::copy_elems( const Queue& c )
    {
    	for( QueueItem< Type >* pt = orig.head; pt; pt = pt->next )
    		push( pt->item );
    }
    
    template< class Type >
    ostream& operator<<( ostream& os, const Queue< Type > & q )
    {
    	os << "< ";
    	QueueItem< Type >* p;
    
    	for( p = q.head; p; p = p->next )
    		os << p->item << " ";
    	os << " >";
    	return os;
    }
    
    template< class Type >
    istream& operator>>( istream& in, Queue< Type >& q )
    {
    	Type temp;
    	in >> temp;
    	q.push( temp );
    	return in;
    }
    
    template< class Type > template< class Iter >
    void Queue< Type >::assign( Iter beg, Iter end )
    {
    	destroy();
    	copy_elems( beg, end );
    }
    
    template< class Type > template< class Iter >
    void Queue< Type >::copy_elems( Iter beg, Iter end )
    {
    	while( beg != end )
    	{
    		push( *beg );
    		++beg;
    	}
    }
    

    Hoffe ihr könnt mir jetzt vielleich helfen=)

    Lg freeG



  • Templates werden nur zu Bytecode kompiliert, wenn sie instantiiert werden. Da du das aber nicht in Template.cpp machst, ist sie theoretisch leer. Wenn nun in einer anderen Übersetzungseinheit dieses Template instantiiert werden soll meckert der Compiler zwar eigentlich nicht, da er die Deklarationen/Klassendefinitionen im Header hat, aber der Linker, weil er die Implementierungen nicht finden kann. Deshalb müssen Templates immer in Headern stehen. Da Header aber nur für Deklarationen gebraucht werden sollten, um nicht alles mehrfach zu kompilieren, sagt man einfach, es gibt Files wie *.impl, *.tpl etc., die aber wie Header included werden. Es sind - auf deutsch - Header.



  • Ad aCTa schrieb:

    Templates werden nur zu Bytecode kompiliert, wenn sie instantiiert werden.

    Er hat das Wort gesagt. Steinigt ihn! 🤡
    http://de.wikipedia.org/wiki/Bytecode


  • Mod

    volkard schrieb:

    Ad aCTa schrieb:

    Templates werden nur zu Bytecode kompiliert, wenn sie instantiiert werden.

    Er hat das Wort gesagt. Steinigt ihn! 🤡
    http://de.wikipedia.org/wiki/Bytecode

    Ist eigentlich vom Standard garantiert, dass der Compiler keinen Bytecode erzeugt?

    @fr33g: Da sind, wenn ich das richtig sehe, auch noch ein paar andere Fehler drin. Aber ein paar zu viele, um die jetzt alle zu kommentieren oder gar zu berichtigen. Mein Compiler schmeißt jedenfalls an die 50 Warnungen und Fehler.



  • So erst mal danke für die Antworten, sorry war eben ein bissel in Eile.
    Hab jetzt nochmal alles bei mir getestet, folgendet Code funktioniert einwandfrei wenn ich für die Implementierung eine .txx Datei nehme, nehme ich eine .cpp-Datei erhalte ich Fehler.

    Hier nochmal der Code, hoffe ist nicht zu lang:-P

    Schonmal vielen Dank für Eure Hilfe und Mühe

    main.cpp

    #include <iostream>
    #include "bla.h"
    
    using namespace std;
    
    int main()
    {
    	Queue< int > qi;
    	for( int i = 0; i < 10; ++i )
    		qi.push( i );
    
    	cout << qi;
    
    	cin.get();
    }
    

    bla.h

    #ifndef BLA_H
    #define BLA_H
    
    #include <list>
    #include <iostream>
    
    template< class Type > class Queue
    {
    	public:
    		template< class Type >friend std::ostream& operator<<( std::ostream&,  const Queue< Type >& );
    		Queue();
    		Queue( const Queue& );
    		Queue& operator=( const Queue& );
    		~Queue();
    		Type& front() { return data.front(); }
    		const Type& front() const { return data.front(); }
    		void push( const Type& );
    		void pop();
    		bool empty() const;
    
    	private:
    		std::list< Type >data;
    		void destroy();
    		void copy_elems( const Queue& );
    };
    
    #include "bla.txx"
    
    #endif
    

    bla.cpp

    #include <list>
    #include <iostream>
    
    using std::ostream;
    using std::list;
    
    template< class Type >
    Queue< Type >::Queue()
    	{
    	}
    
    template< class Type >
    Queue< Type >::Queue( const Queue& orig )
    	{
    		copy_elems( orig );
    	}
    
    template< class Type >
    Queue< Type >::~Queue()
    {
    	destroy();
    }
    
    template< class Type >
    Queue< Type >& Queue< Type >::operator=( const Queue& rhs )
    {
    	if( this != &rhs )
    	{
    		destroy();
    		copy_elems( rhs );
    	}
    	return *this;
    }
    
    template< class Type >
    void Queue< Type >::push( const Type& dat )
    {
    	data.push_back( dat );
    }
    
    template< class Type >
    void Queue< Type >::pop()
    {
    	data.pop();
    }
    
    template< class Type >
    bool Queue< Type >::empty() const
    {
    	return data.size() == 0;
    }
    
    template< class Type >
    void Queue< Type >::destroy()
    {
    	data.clear();
    }
    
    template< class Type >
    void Queue< Type >::copy_elems( const Queue& q )
    {
    	data.assign( q.data.begin(); q.data.end() );
    }
    
    template< class Type >
    ostream& operator<<( ostream& os, const Queue< Type > & q )
    {
    	os << "< ";
    
    	for( typename list< Type >::const_iterator it = q.data.begin(); it != q.data.end(); ++it )
    		os << *it;
    	os << " >";
    	return os;
    }
    

    Folgende Fehler enstehen bei mir:

    1>e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(8): error C2143: Syntaxfehler: Es fehlt ';' vor '<'
    1>e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(8): error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt.
    1>e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(8): error C2988: Unerkannte Vorlagendeklaration/-definition
    1>e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(8): error C2059: Syntaxfehler: '<'
    1>e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(14): error C2143: Syntaxfehler: Es fehlt ';' vor '{'
    1>e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(14): error C2447: '{': Funktionsheader fehlt - Parameterliste im alten Stil?
    1>e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(19): error C2143: Syntaxfehler: Es fehlt ';' vor '<'
    1>e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(19): error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt.
    1>e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(19): error C2086: 'int Queue': Neudefinition
    1>          e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(8): Siehe Deklaration von 'Queue'
    1>e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(19): error C2988: Unerkannte Vorlagendeklaration/-definition
    1>e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(19): error C2059: Syntaxfehler: '<'
    1>e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(19): error C2588: "::~Queue": unzulässiger globaler Destruktor.
    1>e:\sources\übungsprogramme\von büchern und tutorials\test\test\bla.cpp(19): fatal error C1903: Weiterverarbeitung nach vorherigem Fehler nicht möglich; Kompilierung wird abgebrochen.
    

    Lg freeG



  • Blanke Vermutung: während des Compilierens generiert der Compiler aus allen cpp-Dateien Objektdateien (er kompiliert sie). Dabei ist in der konkreten Datei die Klasse Queue natürlich unbekannt, deshalb die Fehler. Dass man in diesem Fall die Kompilierung gar nicht will, weil die cpp-Datei in den Header eingebunden wird, weiß der Compiler nicht.
    Bei jeder anderen Dateiendung (außer evntl. c) unterlässt der Compiler das Erzeugen der Objektdatei, also ist es egal ob txx, inl, impl usw., nur kein cpp.



  • Du darfst Templates nicht in .cpp-Dateien definieren, wenn sie über mehrere Module hinweg benötigt werden (Genaueres siehe FAQ). Schreib also die Klassendefinition mit Funktionsdeklarationen in eine .hpp-Datei, und die Funktionsdefinitionen in eine .inl- oder .impl-Datei (die Endungen dürften am verbreitetsten sein). Diese bindest du am Ende der .hpp-Datei ein.

    Natürlich gibt es Fehler, wenn du versuchst, Memberfunktionen ohne Klassendefinition zu definieren.



  • Ok danke für die Antworten, hört sich auch logisch an 😃

    Lg freeG


Log in to reply