template parameter einschränken



  • .filmor schrieb:

    Es geht nicht (zumindest nicht so, dass es sich nicht umgehen ließe), ist aber auch nicht nötig. Warum sollte man das wollen?

    Nennt sich "Generic" und soll zur Codesicherheit bzw. zur vermeidung von Fehler beitragen. Die Umsetzung ist rein auf der Sourcecodeseite. Der Compiler wandelt erst die Generic's auf die unterste Ebene um (z.B. Java: java.lang.Object) und ruft danach den Postprozessor auf (sofern vorhanden 😉 ). Moderne Sprachen wie Java oder C# besitzen eine derartige Feature und es gehört meiner Meinung nach zu jeder Objekt-Orientierten-Programmiersprache dazu. Wieso C++ dies nicht besitzt liegt wohl an der Vergangenheit. Es könnte jedoch ohne Probleme eingeführt werden.


  • Mod

    FreakyBKA schrieb:

    ich wollte mal fragen ob es möglich ist die template parameter einzuschränken, soll heißen dass die zum beispiel von einer bestimmten klasse abgeleitet sind, um so gewissen funktionalitäten zu erzwingen.
    wenn ich also eine klasse A habe und eine template Klasse B<t> und ich möchte, dass der templateparameter t von A abgeleitet sein muss.
    geht das?
    danke schon mal im voraus.

    Mit anderen Worten, falls jemand das Template B mit einem Argument t zu instantiieren versucht, das nicht von A abgeleitet ist, soll der Compiler mit einer Fehlermeldung abbrechen? Ja, das geht - indem wir das Template so gestalten, dass es ein Konstrukt enthält, dass bei unpassendem Templateargument fehlerhaft wird; dazu u.a. sind static asserts da (in boost und demnächst im Standard). Einige Leute scheinen sich nicht klar zu machen, was "erzwingen" eigentlich heißen soll.


  • Administrator

    Es gibt keinen vernünftigen Grund, wieso man in C++ einen Templateparamter zusätzlich einschränken möchte. Eine Einschränkung findet automatisch statt.
    Die Templates in C++ haben im übrigen relativ wenig mit den Java Generics zu tun!

    Darüber gab es schon ein paar mal eine Diskussion, z.B. die hier:
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-222307.html

    @Siassei,
    Wäre sinnvoll, wenn du den Thread auch liest und dich ein wenig über Templates in C++ informierst! Es wäre eine absolute Idiotie, wenn man sowas in C++ einführen würde. Zudem ist es mir ein Rätsel, was du unter einer modernen Sprache verstehst. Meiner Meinung nach sind das Definitionen, welche sehr wage sind und meistens nur zu Flamewars führen.

    Grüssli



  • camper schrieb:

    indem wir das Template so gestalten, dass es ein Konstrukt enthält, dass bei unpassendem Templateargument fehlerhaft wird; dazu u.a. sind static asserts da (in boost und demnächst im Standard).

    Ja, aber das hält ihn immernoch nicht davon ab, das Template zu spezialisieren (das meinte ich mit "Umgehen").

    @Siassei: Wenn der Templateparameter die Anforderungen bezüglich der Schnittstelle nicht erfüllt dann gibt es einen Compilerfehler (mit Konzepten sogar einen, aus dem man den Fehler ablesen kann ;)). Das sollte zur Fehlervermeidung reichen.



  • Dravere schrieb:

    Es gibt keinen vernünftigen Grund, wieso man in C++ einen Templateparamter zusätzlich einschränken möchte.

    Nun ein Grund wäre, es überhaupt übersetzbar zu machen. Im einfachsten Fall hat man eine Template-Funktion

    template< typename T >
    void machWas( const T& x );
    

    Angenommen es gebe zwei unterschiedlichen Implementierungen - eine für alle int-Typen - und eine etwas andere für double und float. Dann kann man z.B. mit enable_if aus boost oder dem tr1 erreichen, dass man beide auch übersetzen und benutzen kann.

    Gruß
    Werner



  • Für nur zwei Implementierungen lohnt sich ein Template - das eigentlich dazu konzipiert wurde, mit jedem Typ zu funktionieren - nicht wirklich. Da gibt es bessere Möglichkeiten, zum Beispiel Überladung.


  • Administrator

    @Werner Salomon,
    Das hat aber nichts mit zustäzlicher Einschränkung des Templatesparameters zu tun, sondern eher mit Templatespezialisierung. Man könnte allenfalls sogar eine Überladung nehmen. Oder ich habe dein Beispiel nicht verstanden.

    Und ich möchte vor allem auf das "zusätzliche" hinweisen. Templateparameter sind von Haus aus eingeschränkt, da sie ein gewisses Konzept zur Verfügung stellen müssen. Ich habe nur gesagt, dass diese zusätzliche Einschränkung unnötig sei.

    Grüssli



  • @Nexus, @Dravere
    lest vielleicht das Kapitel Background hinter dem boost-Link. Dort ist es mit dem Beispiel 'negate' erklärt. Die Einschränkung ist notwendig, damit der Compiler die 'richtige' Version auswählt.

    @Nexus - auch in meinem Beispiel mit int-Typ oder Fließkomma-Typ sind es zwar zwei Implementierungen eines Templates aber faktisch beliebig viele Instanziierungen, wenn man eigene Zahlentypen mit hinzuzählt. D.h. ein Überladen ist nicht wirklich eine Lösung, sondern führt nur zu einer Vervielfältigung von Sourcecode.

    Gruß
    Werner


  • Administrator

    @Werner Salomon,
    Also du meinst Kapitel 1.2 Background? Dort wird aber nur das folgende erklärt:
    SFINAE (substitution-failure-is-not-an-error)
    Eine ganz entscheidende Regel von den Templates, definitiv ...
    Was das aber nun mit dem Thema im Thread zu tun hat oder deinem Beispiel ist mir völlig Schleierhaft. Ich seh immer noch kein Grund, wieso man einen Templateparameter verbieten möchte, welcher aber das Konzept erfüllt, welches verlangt wird.

    Grüssli



  • Dravere schrieb:

    @Werner Salomon,
    Also du meinst Kapitel 1.2 Background? Dort wird aber nur das folgende erklärt:
    SFINAE (substitution-failure-is-not-an-error)
    Eine ganz entscheidende Regel von den Templates, definitiv ...
    Was das aber nun mit dem Thema im Thread zu tun hat oder deinem Beispiel ist mir völlig Schleierhaft.

    stimmt, Du hast Recht. Ich hatte das Kapitel 1.2 völlig missverstanden und für ein Beispiel gehalten.

    Dravere schrieb:

    Ich seh immer noch kein Grund, wieso man einen Templateparameter verbieten möchte, welcher aber das Konzept erfüllt, welches verlangt wird.

    Ich finde z.Zt. kein geeignetes Beispiel mit den zwei Templates (s.Beispiel in meinem vorletzten Beitrag), aber mir ist noch ein anderer Grund eingefallen. Es kann nämlich vorkommen, dass Typen sich zwar übersetzen lassen, der Code aber nicht in jedem Fall dass tut, was der Autor erwartete.
    Man nehme z.B. einen Stream der im Binär-Format serialisieren soll, und zwar so, wie die Variablen im Speicher stehen. Also vom Prinzip her etwa:

    class obinstream : public std::basic_ios< char >
    {
    public:
        explicit obinstream( std::streambuf* sb = 0 )
            : std::basic_ios< char >( sb )
        {}
        obinstream& operator<<( int x )
        {
            rdbuf()->sputn( reinterpret_cast< const char* >( &x ), sizeof( x ) );
            return *this;
        }
        // Bem.: hier ohne Fehlerbehandlung! bitte so nicht verwenden
    };
    

    Das braucht man jetzt nicht nur für int sondern auch für unsigned int, long, unsigned long, short usw. Der Code ist immer gleich, nur der Typ ändert sich, also die ideale Anwendung für ein Template:

    class obinstream : public std::basic_ios< char >
    {
    public:
        explicit obinstream( std::streambuf* sb = 0 )
            : std::basic_ios< char >( sb )
        {}
        template< typename T >
        obinstream& operator<<( const T& x )
        {
            rdbuf()->sputn( reinterpret_cast< const char* >( &x ), sizeof( x ) );
            return *this;
        }
    };
    

    .. funktioniert für die oben aufgezählten Typen und alles scheint ok, bis jemand schreibt:

    obinstream out( .. );
        out << std::string("23");
    

    Das lässt sich generieren, es stürzt nicht gleich ab, wenn man es laufen lässt und das Problem wird erst ersichtlich, wenn man versucht den String wieder aus dem Device heraus zu lesen.

    Um das zu verhindern schränkt man das Template-Argument auf die geplanten Typen ein.

    #include <boost/utility/enable_if.hpp>
    #include <boost/type_traits.hpp> // is_arithmetic
    
    class obinstream : public std::basic_ios< char >
    {
    public:
        explicit obinstream( std::streambuf* sb = 0 )
            : std::basic_ios< char >( sb )
        {}
        template< typename T >
        typename boost::enable_if< typename boost::is_arithmetic< T >, obinstream& >::type operator<<( const T& x )
        {
            rdbuf()->sputn( reinterpret_cast< const char* >( &x ), sizeof( x ) );
            return *this;
        }
    };
    

    Das hat zwei wesentliche Vorteile. Zum einen lässt sich der Code mit dem string nicht mehr compilieren und zum anderen tritt der Fehler genau da auf, wo jemand versucht den string in den obinstream zu schieben.

    Gruß
    Werner



  • Oder man nimmt die Holzhammermethode:

    class DieseKlasseDarf{
    
    public:
      void NurDankDieserMethodeDarfIchDasTemplateBenutzen(){ }
    
    };
    
    template<typename T>
    void TemplateFunktion(T &t){
      t.NurDankDieserMethodeDarfIchDasTemplateBenutzen();
      // Rest
    }
    

    SCNR 😃


Anmelden zum Antworten