Zeiger in Zeiger konvertieren - Templates



  • Kellerautomat schrieb:

    Adressoperator überladen :p

    Zeig mal, was du dir da vorstellst. Mir fällt nichts sinnvolles ein... 😉



  • Wie ich schon befürchtet habe, gehört wohl meine Absicht eher zu den Tabus. Tatsächlich - so muss ich zugeben - ist es nicht gerade ratsam "Test<int>" in "Test<void>" umzuwandeln, insbesondere deswegen, weil "void" bei einigen Methoden zu unerlaubten Zugriffen führen könnte.

    Mein Fehler war, dass ich - was ja allgemein ziemlich oft vorkommt - eine Frage gestellt habe, die an meinem eigentlichen Problem vorbeigeht:

    In Java spricht man von einer sog. uneingeschränkten parametrischen Polymorphie, wenn man den Typ einer generischen Klasse (Klassen-Template) nicht genauer spezifiziert. Das kann z.B. vorkommen, wenn man eine Funktion definiert, die jede Art von Listen als Parameter annehmen soll, also Integer-Listen, Float-Listen usw.

    In C++ geht das ja ebenso wunderbar mit dem Schlüsselwort "template". Nun wurde mir in einigen Büchern eingeimpft, dass ein Funktions-Template für jeden Typ neu generiert wird und damit bei wachsendem Code immer mehr Speicher beansprucht. Und eben das möchte ich vermeiden.

    Um zurück zum Beispiel zu kommen. Was ich eigentlich vorhatte, ist folgendes:

    void irgendeineFunktion(Test<void>* x)
    {
      ...
    }
    

    Würde nun die automatische Typen-Konversion von "Test<irgendwas>" in "Test<void>" funktionieren, dann hätte ich eine Funktion, die jeden Typ von "Test" annehmen könnte.

    Um eine solche "unschöne" Konversion zu vermeiden, ist natürlich "template" die Lösung:

    template <typename T>
    void irgendeineFunktion(Test<T>* x)
    {
      ...
    }
    

    Hier würde jedoch theoretisch (es kann sein, dass ich mich irre) der Compiler bei jedem Zeigertyp von Test eine neue Funktion generieren.

    Meine eigentliche Frage lautet also: Ist es in diesem Fall möglich, nur eine Funktion generieren zu lassen (wenn ja, wie?), oder ist der Compiler schon schlau genug, das selbst zu erkennen?

    Gruß Josef



  • Generics und Templates sind nunmal zwei komplett verschiedene Dinge. Generics eignen sich nicht für viel mehr als Container-of-Type-T und sind ein Laufzeitmachanismus, zusammengehalten durch dynamische Bindung (aka Interfaces), während Templates ein Compilezeitmechanismus mit impliziten Interfaces (In C++1y vielleicht Concepts) sind. Es ist bei Templates grundsätzlich nicht möglich, für verschiedene Typen den selben Code zu erzeugen, weil diese im allgemeinen in keiner gemeinsamen Vererbunshierarchie sind und Funkionssignaturen nicht exakt festgelegt sind, sondern nur ungefähr passen müssen (implizite Konvertierungen). Deshalb braucht man unter anderem auch Boxing in Java. (Typen müssen von Object abgeleitet sein, das trifft auf Wrapperklassen zu, auf primitive Datentypen nicht)

    Abgesehen davon: Warum machst du dir überhaupt Gedanken darüber, wie oft eine Funktion generiert wird? In der heutigen Zeit haben wir so viel Speicher zur Verfügung, da spielt das keine Rolle. Wenn der Compiler das optimieren kann, wird er das von selbst tun.



  • Danke für die Informationen. So genau habe ich den Unterschied zwischen Generics und Templates nun auch nicht gekannt, auch wenn mir bereits klar war, dass Java und C++ zwei grundlegend verschiedene Dinge sind.

    Dazu, wieso ich mir darüber Gedanken mache: Allgemein ist es sicherlich nicht schädlich, etwas genauer unter die Lupe zu nehmen und zu hinterfragen. Im Gegenteil, Strukturwissenschaften wie Mathematik wachsen ja gerade durch den Zweifelnden heran (ich verweise dabei auf die Mathematikgeschichte der 30er).

    Nun gibt es aber den Zweifelnden, der keine Fachkenntnisse hat und bezweifelt was schon längst bewiesen ist, und den, der seines Faches mächtig ist und das für selbstverständlich hält, das der ungebildete dennoch hinterfragt.

    Weil sich eben gezeigt hat, dass ich Generics und Templates noch nicht in Hülle und Fülle begreife, ordne ich mich mal diesbezüglich dem ersteren zu.
    Nichtsdestotrotz kann und will ich nicht befürworten, dass man (meinen Ausdruck bitte nicht offensiv auffassen) schlampig programmieren soll, nur weil das Schlampigsein sich für den Endnutzer kaum mehr bemerkbar mache. Natürlich wurde das hier von keinem behauptet noch angedeutet, aber schließlich führt das genau auf eben diesen Trott hinaus, den so viele Programmierer pflegen. Speicherverbrauch hin oder her, dass Festplatten und Arbeitsspeicher alles schlucken können, heißt noch lange nicht, dass wir fraglos literweise Maschinencode verabreichen sollen, wenn auch genauso ein Tropfen mit dem wesentlichsten reichen würde.

    Ob nun jemand seinen Programmcode und die Ausgabe optimiert oder nicht, bleibt seine Entscheidung. Aber wenn eine Programmiersprache nicht die nötigen Schnittstellen bietet (jetzt mal abgesehen von inline ASM), die Wünsche des Programmierers zu realisieren, dann ist das beider Fehler: Fehler des Sprachkonzepts, das nicht alle Möglichkeiten eröffnet, und Fehler des Programmierers, der diese Sprache für seine Zwecke gewählt hat. Mir ist es nur leid, ständig diesen Fehler zu begehen.

    Ehrlich gesagt, habe ich gerade und auch später nicht die Zeit dazu, das Thema weiter auszuführen oder gar in einer Diskussion vollständig aufzurollen. Ich wollte mich lediglich hinsichtlich meiner Gedanken erklären, die hier auf Verwunderung stoßen.

    Gruß Josef



  • Du hast schon recht, dass das executable vermutlich etwas größer wird wenn man ein Funktionstemplate mit verschiedenen Datentypen instanziiert. (Außer der Compiler macht da noch irgendwas tolles, und Compiler machen oft viel tollere Sachen als man denkt.) Da beim RAM-Verbrauch aber die Daten meist viel mehr ausmachen als der code (beim Festplattenverbrauch genauso), würde ich so ein Pointer-Gefrickel nur um ein paar Bytes in der Exe zu sparen erstmal für premature optimization (die ja bekanntlich die root of all evil ist ;)) halten. Klar, wenn du tatsächlich den Fall hast, dass das nötig ist, kann man sowas machen, aber sonst würde ich doch den code, der einfacher zu verstehen, zu warten und zu erweitern ist, erstmal bevorzugen.



  • Ich glaube, ich habe irgendwo mal einen Artikel darüber gelesen, wie viel Speicher generierte Spezialisierungen bei bestimmten Anzahlen verbrauchen, dabei kam AFAIR heraus dass der Compiler da mit optimiert.



  • @Sone: Interessant. Das bestärkt meine Hoffnung auf

    Außer der Compiler macht da noch irgendwas tolles, und Compiler machen oft viel tollere Sachen als man denkt.

    😉



  • Warum ist dir eigentlich so wichtig, dass das executable klein ist? Das wird hier ja kaum um Größenordnungen gehen sondern eher um ein paar Promille bis Prozent, jenachdem was du machst. 64 intro (Demoscene)? 🙂



  • zumal auch solche konvertierungen die du dir erhoffst nicht unbedingt vom compiler genutzt werden, da dieser ja ohne jegliche hürden den typ bestimmt und dafür eine funktions signatur sucht. sprich, es kann sein, dass er trotz deines schönen void* templates doch auf eigene faust andere instantiierungen bildet



  • Mir geht es ja nicht um die paar Promille, sondern einfach ums Prinzip. Wenn es schlanker und schneller geht, dann soll es eben schlanker und schneller sein, da spricht ja nichts dagegen. Ich finde es nur schade, wenn man daran gehindert wird. Vielleicht (jetzt kommt der Freud in mir) liegt es ja auch an meinem Hang fürs Axiomatisieren. 😃



  • Beim Programmieren geht man nunmal Kompromisse ein. Der klassische ist size vs. speed, in dem Fall denke ich aber eher an Code-Einfachheit vs. size. Und für mich ist eleganter, simpler code erstmal das wichtigste (so ganz axiomatisch ;)), wovon ich nur abweiche wenn es nötig ist. Andere Leute, die den source mal irgendwann bearbeiten wollen/müssen (oder gar man selbst nach einiger Zeit) freuen sich da ja (bzw. ärgern sich ziemlich wenns nicht so ist).



  • pureprogger schrieb:

    Wenn es schlanker und schneller geht, dann soll es eben schlanker und schneller sein

    Dann nimm doch optimierten inline-assembler 🤡



  • Da kann ich dir nur zustimmen, Dobi. Sauberer Code geht natürlich immer vor. 🙂

    @Sone: 😃



  • Mal abgesehen davon sind Templates schneller als Generics...



  • Kellerautomat schrieb:

    Mal abgesehen davon sind Templates schneller als Generics...

    Laut dem was ich im Netz lese, sind Templates eine Form der Unterstützung von Generics.



  • Darf ich dich bitten, zuerst den Thread zu lesen, bevor du schreibst?

    Kellerautomat schrieb:

    Generics und Templates sind nunmal zwei komplett verschiedene Dinge. [...]



  • Kellerautomat schrieb:

    Darf ich dich bitten, zuerst den Thread zu lesen, bevor du schreibst?

    Kellerautomat schrieb:

    Generics und Templates sind nunmal zwei komplett verschiedene Dinge. [...]

    Vielen Dank, ich habe den Thread gelesen.
    Falls du Recht haben solltest, kannst du den Wikipedia-Eintrag editieren.



  • Bei Programmiersprachen gibt es prinzipiell zwei Ansätze, generische Typen zu unterstützen. Der eine ist es, dass GenerischerTyp<T> für alle Ts denselben Code verwendet und über dynamische Bindung von Methoden spezifische Eigenschaften von T nutzt. Ein anderer Weg, den u.A. C++ geht, ist die Anwendung von Templates. Dabei wird der Typparameter zur Compilierzeit direkt mit dem konkreten Typ ersetzt.



  • Kellerautomat schrieb:

    Bei Programmiersprachen gibt es prinzipiell zwei Ansätze, generische Typen zu unterstützen. Der eine ist es, dass GenerischerTyp<T> für alle Ts denselben Code verwendet und über dynamische Bindung von Methoden spezifische Eigenschaften von T nutzt. Ein anderer Weg, den u.A. C++ geht, ist die Anwendung von Templates. Dabei wird der Typparameter zur Compilierzeit direkt mit dem konkreten Typ ersetzt.

    Halt, da hab' ich wohl falsch verstanden. Ich dachte, Generics sind generische Typen. Dabei sind, wie du schon angedeutet hast, Generics und Templates die Umsetzung von generischen Typen. 💡 wurde so im Artikel falsch impliziert.

    Danke.


Anmelden zum Antworten