Template-Rekursion funktioniert nicht



  • Warum könnte folgendes nicht funktionieren? Der Compiler wird einfach nicht fertig. Habe die unwichtigen Sachen rausgenommen...

    template<unsigned N, typename Type>
    class foo
    {
    	template<unsigned I>
    	struct sum {
    		static void result(Type& res)
    		{
    			res += /*...*/;
    			if(I < N) // Liegt es hieran?
    				sum<I + 1>::result(res);
    		}
    	};
    
    // ...
    
    	Type eval() {
    		Type res = 0;
    		sum<0>::result(res);
    		return res;
    	}
    };
    

    Danke!



  • Ich glaube ??? da fehlt irgendwo das return; 🙂 Sonst gehts ja immer weiter ohne das die Rekursive Funktion mal wieder zurückläuft...



  • Das kann so nicht gehen. Wie du schon richtig festgestellt hast, liegt es an dem if. Der kompiler muss in jedem Fall beide teile abarbeiten. Du musst ein neues template machen. Außerdem ist das mit den Funktionen nicht so schlau, nimm doch besser klassenstatische Konstanten... So würde ich das lösen...

    namespace help
    {
        template<bool, unsigned i,unsigned r1,unsigned n>
        class h
        {
        private:
            static const unsigned rt=r1+...;
        public:
            static const unsigned r=h<(i<n),i+1,rt,n>::r;
        };
    
        template<unsigned i,unsigned r1,unsigned n>
        class h<false,i,r1,n>
        {
        public:
            static const unsigned r=r1;
        };
    };
    
    template<unsigned n>
    class foo
    {
    private:
        template<unsigned i,unsigned r>
        class sum
        {
        private:
            static const unsigned r1=r+...;    //wenn das dein compiler nicht kann: enum{r=r+...};
        public:
            static const unsigned r=help::h<(i<n),i+1,r1,n>::r;
        };
    public:
    };
    

    Keine Garantie auf Fehlerfreiheit. Template-metaprogrammierung is zwar eas schönes, aber leider sehr Fehleranfällig...



  • Hallo,
    mit deiner in dem Kommentar geäußerten Vermutung liegst du richtig. Alle Zweige einer Bedingung werden vom Compiler ausgewertet. Zur Laufzeit wird dann entschieden, welcher betreten wird.

    Die Auswertung sum<I + 1>::result(res); zwingt den Compiler das Template zu instanziieren und das geht rekursiv ins unendliche.

    Lösen kannst du das ganze mit (partieller) Spezialisierung bzw. Überladung.

    Hier mal eine VC 6.0 konforme Lösung:

    template <unsigned i>
    struct Int2Type {};
    
    template<unsigned N, typename Type>
    class foo
    {
       struct sum {
            template <unsigned i>
            static void result(Type& res, Int2Type<i>, double)
            {
                res += /*...*/;
    
                sum::result(res, Int2Type<i + 1>(), 0);            
            }
            static void result(Type& res, Int2Type<N>, int) {}
        };
    
    // ...
        Type eval() {
            Type res = 0;
            sum::result(res, Int2Type<0>(), 0);
            return res;
        }
    };
    


  • ness: Das Problem ist, dass die Summenelemente nicht zur Compilezeit konstant sind. Deswegen mache ich das so.

    HumeSikkins: Danke, ich probier es gleich aus.



  • Es funktioniert prima und sauschnell 🙂 *freu*. Danke nochmal an alle!



  • Noch eine Frage: Muss ich die Struct drumbasteln oder könnte ich sofort Member-Funktionen unrollen, wenn in sum beispielsweise Zugriffe auf Members von foo erfolgen sollen?



  • 😞 scheinbar nicht. Er sagt, dass es dann zuviele Überladungen für den this-Zeiger gibt, wenn ich die Funktionen nicht-statisch mache.



  • Login vergessen schrieb:

    Noch eine Frage: Muss ich die Struct drumbasteln oder könnte ich sofort Member-Funktionen unrollen, wenn in sum beispielsweise Zugriffe auf Members von foo erfolgen sollen?

    sum brauchst du hier nicht. Du kannst auch einfach:

    template <unsigned i>
    struct Int2Type {};
    
    template<unsigned N, typename Type>
    class foo
    {
        template <unsigned i>
        static void result(Type& res, Int2Type<i>, double)
        {
            res += /*...*/;
            result(res, Int2Type<i + 1>(), 0);            
        }
        static void result(Type& res, Int2Type<N>, int) {}
    // ...
        Type eval() {
            Type res = 0;
            result(res, Int2Type<0>(), 0);
            return res;
        }
    };
    

    Oder auch:

    template <unsigned i>
    struct Int2Type {};
    
    template<unsigned N, typename Type>
    class foo
    {
        template <unsigned i>
        void result(Type& res, Int2Type<i>, double)
        {
            res += /*...*/;
            result(res, Int2Type<i + 1>(), 0);            
        }
        void result(Type& res, Int2Type<N>, int) {}
    // ...
        Type eval() {
            Type res = 0;
            result(res, Int2Type<0>(), 0);
            return res;
        }
    };
    

Anmelden zum Antworten