Templates Portierungsprobleme Linux zu Windows



  • Hallo Leute,

    ich portiere gerade ein Projekt von Linux nach Windows. Als Compiler und IDE nutze ich MSVC 2008 SP1. Nun habe ich eine Datei welche unter Linux (Ubuntu 8.04, ich glaube GCC 4.2) einwandfrei läuft, aber der MS Compiler mag sie nicht.

    Er bemängelt das ein Templatebezeichner (T) in einer Nested Class nicht deklariert ist. Der Bezeichner stammt aus der äußeren Klasse.
    Anbei ist ein ausführbarer Auszug auf dem problematischen Code.

    VariableChildTree.hpp:

    #include <iostream>
    #include <boost/weak_ptr.hpp>
    #include <boost/shared_ptr.hpp>
    
    using boost::shared_ptr;
    using boost::weak_ptr;
    
    template<class T> class VariableChildTree;
    
    template <class T>
    class VariableChildTree
    {
        public:
            template<class S> class VariableChildTreeIterator;
        private:
            template<class S> class VariableChildTreeNode;
    
        private:
            template <class S>
            class VariableChildTreeNode
            {
                friend class VariableChildTree;
                friend class VariableChildTreeIterator<S>;
    
                public:
    				VariableChildTreeNode(shared_ptr<VariableChildTreeNode<S> > pParent);
                private:
                    shared_ptr<VariableChildTreeNode<S> > m_pParent;
            }; // end class VariableChildTreeNode
    
        public:
            template <class S>
            class VariableChildTreeIterator
            {
                public:
                    friend class VariableChildTree;
                    VariableChildTreeIterator(weak_ptr<VariableChildTreeNode<S> > pWeakCurrNode, weak_ptr<VariableChildTree<T> >  pWeakTree)
    				{
                    	std::cout << "VariableChildTreeIterator::VariableChildTreeIterator" << std::endl;
    					m_pCurrNode = pWeakCurrNode;
    					m_pMyTree = pWeakTree;
    				}
                private:
                    weak_ptr<VariableChildTreeNode<S> >  m_pCurrNode;
                    weak_ptr<VariableChildTree<S> >      m_pMyTree;
            }; // end class VariableChildTreeIterator
    
        public:
            friend class VariableChildTreeIterator<T>;
            friend class VariableChildTreeNode<T>;
    
            static  shared_ptr<VariableChildTree<T> > create();
    
            VariableChildTreeIterator<T> getIterator()
    		{
    			return VariableChildTreeIterator<T>(m_pRootDummy, m_pWeakThis);
    		}
    
        private:
            VariableChildTree();
            weak_ptr<VariableChildTree<T> >                 m_pWeakThis; // a weak pointer to "this" tree, neccessary for weak pointer from iterator to tree
            shared_ptr<VariableChildTreeNode<T> >           m_pRootDummy; // a dummy node for the root node
    }; // end class VariableChildTree
    
    template<class T> template<class S>
    VariableChildTree<T>::VariableChildTreeNode<S>::VariableChildTreeNode(shared_ptr<VariableChildTreeNode<S> > pParent)
    {
        m_pParent = pParent;
    }
    template<class T> VariableChildTree<T>::VariableChildTree()
    {
        m_pRootDummy = shared_ptr<VariableChildTreeNode<T> >(new VariableChildTreeNode<T>());
    }
    template<class T> shared_ptr<VariableChildTree<T> > VariableChildTree<T>::create()
    {
        shared_ptr<VariableChildTree<T> > pTree(new VariableChildTree<T>());
        pTree->m_pWeakThis = pTree;
        return pTree;
    }
    

    und main.cpp:

    #include "VariableChildTree.hpp"
    #include <boost/shared_ptr.hpp>
    
    int main()
    {
    	shared_ptr<VariableChildTree<double> > vct = VariableChildTree<double>::create();
    	vct->getIterator();
    	return 0;
    }
    

    Der Fehler tritt beim aufruf des Konstruktors von VariableChildTreeIterator auf.

    1>d:\...\variablechildtree.hpp(37) : error C2065: 'T': nichtdeklarierter Bezeichner
    1>        d:\...\variablechildtree.hpp(46): Siehe Verweis auf die Instanziierung der gerade kompilierten Klassen-template "VariableChildTree<T>::VariableChildTreeIterator<S>".
    1>        d:\...\variablechildtree.hpp(63): Siehe Verweis auf die Instanziierung der gerade kompilierten Klassen-template "VariableChildTree<T>".
    1>d:\...\variablechildtree.hpp(37) : error C2065: 'T': nichtdeklarierter Bezeichner
    

    Hat vielleicht jemand eine Idee wie man diesen Code unter Windows zum laufen bringt.

    Vielen Dank, sjoe.



  • Gib den Template Parameter doch auch in der nested class an.



  • Lass das <T> doch weg. Innerhalb des Klassentemplates VariableChildTree bezieht sich VariableChildTree immer auf die aktuelle Instantiierung, IIRC.



  • Danke für die Antworten.

    @pumuckl
    Du meinst in Zeile 37 einfach folgendes hinschreiben:

    VariableChildTreeIterator(weak_ptr<VariableChildTreeNode<S> > pWeakCurrNode, weak_ptr<VariableChildTree >  pWeakTree)
    

    Geht leider nicht. Compiler meckert das er ausgehend von Zeile 56 keinen passenden Konstuktor mehr findet.

    @drakon
    Hm, im Kurztest funktioniert das erstmal. Ich werde mal unter Linux testen ob das auch dort funktioniert. Dann müsste ich allerdings sehr viel Code überarbeiten. Naja, so ist das Leben, mal verliert man und mal gewinnen die anderen. 😉

    Für andere Vorschläge bin ich offen.

    Grüße, sjoe.



  • Ich sehs jetzt gerade erst - sicher dass das im Ctor vom iterator ein VariableChildTree<T> sein muss und kein VariableChildTree<S>? Wenn ich richtig liege braucht der Iterator dann allerdings garkeinen Zugriff auf die Interna des VCTree und kann sogar außerhalb der Klasse definiert werden.



  • @pumuckl
    Ja, das ist kein Schreibfehler. Der zweite Parameter ist bewusst von der Oberklasse abhängig. Also leider nichts mit auslagern.

    Grüße, sjoe.



  • Bei Zeiten, werde ich mal im Standard nachschauen, welcher der beiden Compiler nicht standardkonform arbeitet. Aber wahrscheinlich wird das auf undefiniert hinauslaufen, obwohl ich eigentlich denke, dass das, was pumuckl meint doch auch gehen müsste..



  • drakon schrieb:

    Bei Zeiten, werde ich mal im Standard nachschauen, welcher der beiden Compiler nicht standardkonform arbeitet. Aber wahrscheinlich wird das auf undefiniert hinauslaufen, obwohl ich eigentlich denke, dass das, was pumuckl meint doch auch gehen müsste..

    Ggf. in der VCTree-Klasse T als value_type typedeffen bzw. VCTree<T> als tree_type typedeffen und den typedef in der Unterklasse nutzen.



  • Ggf. in der VCTree-Klasse T als value_type typedeffen bzw. VCTree<T> als tree_type typedeffen und den typedef in der Unterklasse nutzen.

    Unglaublich. Damit funktioniert es. Mal schauen ob das auch unter Linux läuft, aber diese Kleinigkeit kann man notfalls auch per ifdef erledigen.
    Vielen Dank nochmals. Der Tip erspart mir eine Menge Arbeit.

    Grüße, sjoe.



  • Ich habe jetzt im Standard nachgeschaut und nichts gefunden, was dagegen spricht. Dann bin habe ich gedacht, dass ich es selbst mal ausprobiere und das kompiliert bei mir wunderbar.. Sofern ich das hier auskommentiere:

    friend class VariableChildTreeIterator<S>;
    

    Ich benutze Visual C++ 2008EE.


  • Mod

    template <class T>
    class VariableChildTree
    {
    ...
        private:
            template<class S> class VariableChildTreeNode;
    ...
    public:
            template <class S>
            class VariableChildTreeIterator
            {
                public:
    ...
                    VariableChildTreeIterator(weak_ptr<VariableChildTreeNode<S> > pWeakCurrNode, weak_ptr<VariableChildTree<T> >  pWeakTree)
    ...
            }; // end class VariableChildTreeIterator
    ...
            friend class VariableChildTreeIterator<T>;
    
    }; // end class VariableChildTree
    

    Die Definition von VariableChildTreeIterator greift auf den in VariableChildTree private deklarierten Bezeichner VariableChildTreeNode zu, aber nur die Spezialisierung VariableChildTreeIterator<S> mit S=T ist friend von VariableChildTree<T>. Das ist möglicherweise ein Fehler, vermutlich aber nicht der Grund, weshalb der Compiler ins Stolpern gerät.



  • Bei der Definition dessen auf das man von der inneren Klasse aus zugreifen kann ist der 98er Standard so weit ich weiß schlampig. (Das heißt er definiert gar nichts.) Im C++0x Standard wird so weit ich das überschauen kann das GCC Verhalten vorgeschrieben.



  • Ben04 schrieb:

    Bei der Definition dessen auf das man von der inneren Klasse aus zugreifen kann ist der 98er Standard so weit ich weiß schlampig. (Das heißt er definiert gar nichts.) Im C++0x Standard wird so weit ich das überschauen kann das GCC Verhalten vorgeschrieben.

    Er schreibt nichts spezielles vor. Sprich er besagt explizit, dass dieseleben Regeln, wie sonst gelten.

    11.8 Nested classes schrieb:

    The members of a nested class have no special access to members of an enclosing class, nor to classes or
    functions that have granted friendship to an enclosing class; the usual access rules (clause 11) shall be
    obeyed. The members of an enclosing class have no special access to members of a nested class; the usual
    access rules (clause 11) shall be obeyed.



  • Wie überall, heißt "kein Zugriff auf private Sachen". Zu mindest kann man das so auslegen und so wurde das auch gemacht. Man kann eine innere Klasse aber so weit ich weiß nicht legal friend machen, womit es keine Möglichkeit gibt den Zugriff zu erlauben. T ist jetzt zwar kein Member aber von Außen kann man trotzdem nicht drauf zugreifen. Ein public typedef T type und dann die Verwendung von type sollte aber pedantisch korrekt sein.


Anmelden zum Antworten