Headereinbindungen bei templates



  • Hallo,

    ich habe ein Problem bei einem Programmierprinzip von C++. Es geht dabei um die "Vorwärtsdeklarationen".

    Prinzipiell deklariere ich ja Klassen, deren Funktionen und Eigenschaften im Header und definiere sie dann in der c Datei.

    Nun benutze ich aber Templates. Ich habe gelesen, dass template Klassen (also deren Methoden) auch innerhalb der Datei, wo sie deklariert werden, definiert werden müssen (aus welchen Gründen auch immer). Und genau hier entstehen die Probleme. Jetzt müsste ich in dem Header meiner Template Klassen andere Header einbinden, was bei Kreuzverweisen irgendwann zu Fehlern führt... Wie kann ich diese Probleme umgehen???

    Grüße



  • Headless schrieb:

    Ich habe gelesen, dass template Klassen (also deren Methoden) auch innerhalb der Datei, wo sie deklariert werden, definiert werden müssen (aus welchen Gründen auch immer).

    Das stimmt nicht. Bei der Verwendung von Templates muss ihre Definition sichtbar sein (damit der Compiler die Instanziierung durchführen kann), doch Deklarationen sind nach wie vor möglich, wo ein unvollständiger Typ erlaubt ist.

    Und abgesehen davon werden Templates auch erst bei der Verwendung instanziiert. Das bedeutet, dass die strenge Deklarationsreihenfolge nicht immer eingehalten werden muss (es reicht, wenn benötigte Typen bei der Instanziierung vollständig sind).



  • Nexus schrieb:

    Das stimmt nicht. Bei der Verwendung von Templates muss ihre Definition sichtbar sein (damit der Compiler die Instanziierung durchführen kann), doch Deklarationen sind nach wie vor möglich, wo ein unvollständiger Typ erlaubt ist.

    Wie genau meinst du das?

    h Datei:

    template<class T> 
    class A
    {
    public: 
    	void meth();
    };
    

    Innerhalb der Methode meth() sollen andere Klassen, die in anderen Headern deklariert sind, benutzbar werden. Sobald ich den #include "XYZ.h" über die h Datei meiner Klassa A schreibe, enstehen unmengen an Fehlern...



  • Headless schrieb:

    Nexus schrieb:

    Das stimmt nicht. Bei der Verwendung von Templates muss ihre Definition sichtbar sein (damit der Compiler die Instanziierung durchführen kann), doch Deklarationen sind nach wie vor möglich, wo ein unvollständiger Typ erlaubt ist.

    Wie genau meinst du das?

    #include <iostream>
    
    struct Foo;
    
    template<typename T>
    void useFooBar(const T& value)
    {
    	Foo foo;
    	foo.bar(value);
    }
    
    struct Foo
    {
    	void bar(int value) { std::cout << "bar " << value << std::endl; }
    };
    
    int main()
    {
    	useFooBar(42);
    }
    

    Wäre useFooBar kein Template, sondern eine normale Funktion, müsste bereits bei der Definition von useFooBar die Funktion Foo::bar bekannt sein. In diesem Fall reicht es aber, wenn Foo::bar in main bekannt ist, da erst hier das Template instanziert wird.





  • Und bei falls der Typ unvollständig sein darf, reicht eine Vorwärtsdeklaration.

    template <typename T>
    class Template;
    
    Template<int>* pointer;
    


  • Ok! Das macht Sinn. Bei mir ist die Struktur, aber um eingies komplexer, wodurch nicht erst in der main instanziiert wird:

    Base.h

    #pragma once
    class Helper;
    class upperBase
    {
    public:
        virtual void use()=0;
    };
    
    template<class T>
    class Base : public upperBase
    {
    public:
        virtual void use()
        {
    		Helper::bar<Concrete>();
    	}
    };
    

    Concrete.h

    #pragma once
    #include "Base.h"
    
    class Concrete : public Base<int>
    {
    public:
    	Concrete();
    };
    

    Concrete.cpp

    #include "stdafx.h"
    #include "Concrete.h"
    
    Concrete::Concrete()
    {
    }
    

    Helper.h

    #pragma once
    static class Helper
    {
    public:
    	template<class P> 
    	static P bar() 
    	{ 
    		P p;
    		return p;	
    	}
    };
    

    Folgende Fehler entstehen:
    error C2027: Verwendung des undefinierten Typs "Helper"
    error C2065: 'bar': nichtdeklarierter Bezeichner
    error C2275: 'Concrete': Ungültige Verwendung dieses Typs als Ausdruck

    In die Base.h "#include "Helper.h" " einzufügen ist leider keine Lösung...auch wenn das in diesem Beispiel funktioniert...



    1. #pragma once ist Compilerabhängig, verwende lieber das übliche #ifdef zeugs.
    2. Eine static class existiert in C++ nicht.


  • Dann schreib halt

    virtual void use();  //ohne Implementierung
    

    und in einen anderen Header, den du dann inkludierst

    #include "Base.h"
    #include "Concrete.h"
    #include "Helper.h"
    
    template<typename T> void Base<T>::use()
    {
        Helper::bar<Concrete>();
    }
    


  • Aber das ist doch auch nur von hinten in die Brust geschossen...

    Wenn ich " #include "Helper.h" " in einem Header benutze, den ich dann wiederum inkludiere, dann kann ich die Helper.h auch direkt inkludieren?!

    @314159265358979:
    2) Mein Compiler beschwert sich aber gar nicht bei der static class?!



  • Headless schrieb:

    1. Mein Compiler beschwert sich aber gar nicht bei der static class?!

    Bist du sicher? Wie auch immer, Compiler dürfen Erweiterungen anbieten. Willst du portables C++ programmieren, verzichte auf static class .



  • Das ist seltsam, welchen Compiler hast du?



  • Da ist nichts in den Kopf geschossen. Nochmal:
    BaseDeclarations.h

    //hier includeguards!!
    
    class upperBase
    {
    public:
        virtual void use()=0;
    };
    
    template<class T>
    class Base : public upperBase
    {
    public:
        virtual void use();
    };
    

    Concrete.h

    //hier includeguards!!
    #include "BaseDeclarations.h"
    
    class Concrete : public Base<int>
    {
    public:
        Concrete();
    };
    

    Helper.h

    //hier includeguards!!
    
    class Helper
    {
    public:
        template<class P>
        static P bar()
        {
            P p;
            return p;   
        }
    };
    

    Base.h

    //hier includeguards!!
    
    #include "Base.h"
    #include "Concrete.h"
    #include "Helper.h"
    
    template<typename T> void Base<T>::use()
    {
        Helper::bar<Concrete>();
    }
    

Anmelden zum Antworten