Templateklasse mit einer spezialisierten Methode - gute Lösung?



  • Ich will je nachdem, ob das Template-Argument ein Pointer-Typ ist, eine kleine angepasste Methode zur Verfügung stellen (falls Pointer, mit Referenz und falls keiner, ohne Referenz). Der Rest der Klasse soll aber gleich sein.

    So sieht meine Klasse im Moment aus, ist das eine gute Lösung ohne auf irgendwelche externen Libs wie boost zurückzugreifen?

    template <class T>
    class foo
    {
        public:
            void bar(const T &value)
            {
                std::cout << "Non-Pointer";
            }
    
            void foobar()
            {
                std::cout << "bar";
            }
    };
    
    template <class T>
    class foo<T *> : public foo<T>
    {
        public:
            void bar(const T value)
            {
                std::cout << "Pointer";
            }
    };
    


  • Warum denn das? Es ist vollkommen sicher, wenn du eine Referenz auf nen pointer übergibst. Da passiert nix böses, manchmal braucht man sowas sogar.



  • IMO mussst du alles neuschreiben.



  • Weil in dem Fall dann die Referenz const wäre, ich den Pointer aber dereferenzieren und den Inhalt des dahinterstehenden Objekts ändern könnte. Genau das will ich aber verhindern 🙂



  • Max0r schrieb:

    Weil in dem Fall dann die Referenz const wäre, ich den Pointer aber dereferenzieren und den Inhalt des dahinterstehenden Objekts ändern könnte. Genau das will ich aber verhindern 🙂

    Bist du sicher? Außerdem hast du Fehler in deinem Beispiel in der spezilisierten Template.



  • Zeus schrieb:

    Max0r schrieb:

    Weil in dem Fall dann die Referenz const wäre, ich den Pointer aber dereferenzieren und den Inhalt des dahinterstehenden Objekts ändern könnte. Genau das will ich aber verhindern 🙂

    Bist du sicher? Außerdem hast du Fehler in deinem Beispiel in der spezilisierten Template.

    Ja bin ich. Wäre nett, wenn du mir die Stelle verraten würdest, an der der Fehler ist.



  • Zeile 21, dein Compiler wird Hinweisen, dass du T* nicht auf T konvertieren kannst.



  • Kann man so machen. Allerdings ist das Problem, dass der übermüdete Programmierer auf die Idee kommen könnte die Basisklasse als polymorphe Basisklasse zu nutzen:

    int main ()
    {
     foo<int*> f1;
     foo<int>* pf = &f1;
     pf->bar (/**/); // na, was ist der statische Typ?
    }
    

    oder noch schlimmer:

    int main ()
    {
     foo<int>* pf = new foo<int*>;
     //...
     delete pf; // hups
    }
    

    Allerdings kann man gegen so einen Missbrauch denke ich nicht all zu viel machen. Sollten aber klar sein.

    Ebenfalls darf nicht vergessen werden, dass das keine Überschreibung der Funktion, sondern lediglich eine Überladung ist. Die Funktion der Basisklasse ist durchaus noch erreichbar:

    int main ()
    {
     foo<int*> f;
     f.foo<int>::bar (/**/);
    }
    

    Glücklicherweise spielt es bei der Überladungsauflösung keine Rolle, aber wenn man will kann man sie dennoch in Betracht ziehen:

    template <class T>
    class foo<T *> : public foo<T>
    {
      using foo<T>::bar;
    
        public:
            void bar(const T value)
            {
                std::cout << "Pointer";
            }
    };
    


  • drakon schrieb:

    Allerdings kann man gegen so einen Missbrauch denke ich nicht all zu viel machen.

    Man kann den Spiess zum Beispiel umdrehen:

    template <typename T>
    class foo_impl
    {
    	public:
    		void bar(const T& value)
            {
                std::cout << "Non-Pointer";
            }
    };
    
    template <typename T>
    class foo_impl<T*>
    {
        public:
            void bar(T value)
            {
                std::cout << "Pointer";
            }
    };
    
    template <typename T>
    class foo : public foo_impl<T>
    {
        public:
            void foobar()
            {
                std::cout << "bar";
            }
    };
    

    Ist zwar etwas mehr Code, aber dafür wird hier keine Vererbung missbraucht. Übrigens interessant, im anderen Thread gings fast um das gleiche. 🙂



  • Naja. Ich habe da ja das benutzen der Basisklasse als Polymorphe Basisklasse und dann das deleten gmeint. Da hast du mit deiner Lösung das gleiche Problem.



  • drakon schrieb:

    Naja. Ich habe da ja das benutzen der Basisklasse als Polymorphe Basisklasse und dann das deleten gmeint. Da hast du mit deiner Lösung das gleiche Problem.

    Wieso? foo_impl ist ein Implementierungsdetail, den Benutzer hat die Klasse nicht zu interessieren.

    Wenn man die Klasse noch in einen detail -Namensraum verpackt, sollte dieser Umstand verdeutlicht werden. Dann noch Fehler zu machen ist Absicht.



  • Jap. Das ist schon klar. Es ist auch so Absicht dahinter eine explizit nicht dafür gedachte Klasse polymorph zu nutzen.. Ich sagte ja, dass man nicht _viel_ dagegen machen kann. In dem Sinne, dass man das nicht verhindern kann, dass man das doch noch standardkonform machen kann. (man kann ja sonst natürlich so ziemlich alles mit irgendwelchen Hacks umgehen).

    Das ganze schön verpacken kann man natürlich, aber dann würde ich vorher eine bessere Alternative für das eigentliche Problem finden wollen.



  • drakon schrieb:

    Es ist auch so Absicht dahinter eine explizit nicht dafür gedachte Klasse polymorph zu nutzen..

    Hat was. 😉
    Oder halt wie du sagst Übermüdung. Dem darf man aber auch detail -Eindringen zutrauen...

    drakon schrieb:

    Das ganze schön verpacken kann man natürlich, aber dann würde ich vorher eine bessere Alternative für das eigentliche Problem finden wollen.

    Grundsätzlich hat man dieses "Problem" immer, wenn man über Metaprogrammierung das Interface einer Klasse bilden will. Da braucht es Vererbung, und im Normalfall eben nicht polymorphe. Daher kann es oftmals sein, dass es keine bessere Alternative gibt.



  • Nexus schrieb:

    drakon schrieb:

    Es ist auch so Absicht dahinter eine explizit nicht dafür gedachte Klasse polymorph zu nutzen..

    Hat was. 😉
    Oder halt wie du sagst Übermüdung. Dem darf man aber auch detail -Eindringen zutrauen...

    Man könnte es ja ganz schlau machen und das ganze in einen namespace 'if_you_ever_use_this_namespace_than_this_is_your_resignation' packen. Ich denke, dass man dann recht sicher sein kann, dass das selbst ein Programmierer mit einer Ordentlichen Menge Alkohol und Drogen nicht braucht. 🙂

    drakon schrieb:

    Das ganze schön verpacken kann man natürlich, aber dann würde ich vorher eine bessere Alternative für das eigentliche Problem finden wollen.

    Grundsätzlich hat man dieses "Problem" immer, wenn man über Metaprogrammierung das Interface einer Klasse bilden will. Da braucht es Vererbung, und im Normalfall eben nicht polymorphe. Daher kann es oftmals sein, dass es keine bessere Alternative gibt.

    Jap. Das stimmt wohl. Ich meinte halt das _eigentliche_ Problem des TO's, dass er uns noch gar nicht verraten hat. 😉 - Ansonsten würde ich es halt eben so machen, wie die STL und wenn mans falsch benutzt, dann ist man selbst schuld.


Anmelden zum Antworten