Skepsis am neuen Standard
-
Nexus schrieb:
Aber schon beim
begin()
kann es zu Problemen führen, da man je nachdem einenconst_iterator
will... Oder?Um ehrlich zu sein: meistens ist mir das total egal.
Und eben befürchte ich, dass man schnell zur Übertreibung neigt, denn
auto
ist fast überall anwendbar, wo eine Initialisierung mit einem Ausdruck stattfindet.Das finde ich zB super.
auto x = 3; auto pos = Object.GetPosition(); // Was ist jetzt pos? Vector<3, float> oder Vector<2, int>? auto size = Blabla.GetSize(); // wie gross ist size? short? int? long? size_t?
Warum ist es wichtig ob pos ein Vector<3, int> oder ein Vector<7, float> ist?
Und warum ist es wichtig welchen unsigned typen size hat? es ist groß genug die größe von Blabla zu beinhalten. wenn ich eine bestimmte größe haben will, dann gebe ich den typen selber an. aber meistens will ich das nicht. vorallem wenn templates im spiel sind.typename vector_type::iterator i = v.begin(); typename vector_type::iterator e = v.end(); while(i!=e) { typename vector_type::value_type copy = *i; copy.foo(); ++i; }
*brr*
viel schöner:
for(auto i=v.begin(), e=v.end(); i!=e; ++i) { auto copy = *i; }
Überall wo du mit templates zu tun hast wird der code soviel klarer durch auto.
vorallem wenn du plötzlich template factories hast:
auto func = create_expression<TypeList<foo, bar, baz, None>>();Es fängt ja schon mit Dingen wie IntelliSense an, wo man sich nicht mehr viel Bezeichner merken muss. Mit
auto
bekommt man auch die Typen geschenkt. An manchen Orten ist die Abstraktion hilfreich, an anderen weniger...Und wo ist das schlecht? Intellisense ist super weil ich mir nix merken muss und auto erspart mir wieder sinnlose information zu tippen.
-
audacia schrieb:
(Praktischer Beweis: Delphi 2009. Das unterstützt Generics, die zur Übersetzungszeit instantiiert werden - sie sind demnach ebenso effizient wie Templates -, doch werden sie dennoch wie gewöhnliche Klassen geparst - was das Problem der nutzlosen Fehlermeldungen, das man in C++ durch Concepts angehen will, vermeidet.)
Ist in C++ nur wegen der fehlenden standardisierten ABI nicht möglich.
Deshalb tut man precompiled header verwenden um den kompilierungsaufwand zu reduzieren.
Aber ich kenne die Templates in Delphi nicht - habe nur sehr gemischtes darüber gehört. Kannst du deren funktionalität mal näher erklären?
-
Vorweg: Kompiliert wird eine generische Klasse erst, wenn sie mit einem konkreten Typen instantiiert wird. Geparst hingegen wird sie schon untypisiert. In den Moduldateien (bei Delphi sind das *.dcu-Dateien) wird dann der Parse-Tree abgelegt, in den bei der Instantiierung nur noch die tatsächlichen Typen injiziert werden müssen.
Nexus schrieb:
Wie kann die volle Typsicherheit in untypisiertem Zustand bewahrt werden?
Durch Typrestriktionen. Bei der Deklaration der generischen Klasse muß der Typ so beschränkt werden, daß zu dessen Übersetzungszeit bekannt ist, welche Operationen dieser Typ beherrscht. Dafür kannst du dann nicht mehr irgendwelche Methoden des Arguments aufrufen, die nicht durch die Typrestriktion explizit bekannt gemacht wurden:
template <class T> bool isEqual (T lhs, T rhs) { return lhs == rhs; } // geht nicht mit Generics
Wenn du aber eine entsprechende Typrestriktion angibst, kannst du so etwas auch mit Generics machen. Bei den Generics in .NET sähe das etwa so aus (Syntax ist Delphi für .NET, da ich nicht flüssig in C# bin):
type CompareHelper <T: IEquatable <T>> = class public class function IsEqual (lhs, rhs: T): Boolean; end; class function CompareHelper <T>.IsEqual (lhs, rhs: T): Boolean; begin Result := lhs.Equals (rhs); end;
Das funktioniert dann auch für skalare Typen, da diese in .NET die nötigen Interfaces unterstützen. (Concept Maps gehen übrigens in eine sehr ähnliche Richtung.)
Da Delphi für Win32 kein "rooted type system" hat, ist das dort nicht so einfach. Gegenwärtig muß man sich mit dem Übergeben eines Comparers etc. behelfen. (Allerdings ist es den Äußerungen der Zuständigen in den CodeGear-Newsgroups zufolge recht wahrscheinlich, daß sich das in den kommenden Versionen noch ändert.)
Shade Of Mine schrieb:
Aber ich kenne die Templates in Delphi nicht - habe nur sehr gemischtes darüber gehört. Kannst du deren funktionalität mal näher erklären?
http://sjrd.developpez.com/delphi/tutoriel/generics/
Shade Of Mine schrieb:
Ist in C++ nur wegen der fehlenden standardisierten ABI nicht möglich.
Wie kommst du denn darauf? Was hat das ABI damit zu tun, daß Templates nicht untypisiert geparst werden können, weil sie nur ein flexibel konfigurierbarer Präprozessormechanismus sind?
Shade Of Mine schrieb:
Deshalb tut man precompiled header verwenden um den kompilierungsaufwand zu reduzieren.
Sagte ich ja bereits. Aber damit ist C++, ohne daß es am Zeilendurchsatz des Compilers läge (s.o.), immer noch deutlich langwieriger zu übersetzen als Sprachen mit einem vernünftigen Modulkonzept, und die Fehlermeldungen sind auch nicht besser.
-
Shade Of Mine schrieb:
Um ehrlich zu sein: meistens ist mir das total egal.
Ob nun ein Iterator volle Zugriffsrechte auf die Elemente hat oder nicht? Wie wird das eigentlich gehandhabt, treten da keine Mehrdeutigkeiten auf?
Shade Of Mine schrieb:
Warum ist es wichtig ob pos ein Vector<3, int> oder ein Vector<7, float> ist?
Naja, es macht schliesslich schon einen Unterschied, ob man mit einem sieben- oder dreidimensionalen Vektor arbeitet. Es ist einfach so, dass man gerade bei diesem Beispiel wissen muss, welcher Typ dahinter steckt, da man ihn anwendet. Und mit
auto
muss ich zuerst die MemberfunktionGetPosition()
suchen und erst ihren Rückgabetypen anschauen, während ich ansonsten gleich bei der Deklaration den Typen sehe.Shade Of Mine schrieb:
Und warum ist es wichtig welchen unsigned typen size hat? es ist groß genug die größe von Blabla zu beinhalten.
Stimmt, das war ein schlechtes Beispiel. Ist aber gerade gut, so wird das leidige Problem der Grössentypen eingeschränkt.
Shade Of Mine schrieb:
Und wo ist das schlecht? Intellisense ist super weil ich mir nix merken muss und auto erspart mir wieder sinnlose information zu tippen.
Ob es gut ist, sich nichts zu merken, kann man wiederum diskutieren. Praktisch ist es während des Programmierens, keine Frage. Aber je mehr man sich auf IntelliSense verlässt, desto abhängiger wird man auch. Mit der Zeit überlegt man sich weniger und schlägt mehr nach.
Shade Of Mine schrieb:
Überall wo du mit templates zu tun hast wird der code soviel klarer durch auto.
Ja, das glaube ich gern. Es gab doch auch mal ein Beispiel für Boost.Lambda, wo ein einfacher Ausdruck bereits einen mehrzeiligen verschachtelten Templatetypen hat.
Ich bin inzwischen schon recht überzeugt, dass
auto
praktisch sein kann, gerade das Size-Beispiel hat mir einen weiteren Vorteil gezeigt. Aber dennoch kann ich die Aussage, dass einen den Typen einer Variable nicht zu interessieren hätte, so nicht unterstützen. Es gibt für mich einfach so viele Fälle, in denen ich froh darüber bin, bei der Deklaration gleich noch den Typen zu sehen. In den Iterationsschleifen weiss ich ja, dass ich einen Iterator habe, da kann ich mir die genaue Deklarationssyntax sparen. Aber bei lokalen Variablen, besonders in kurzen Funktionen, habe ich einfach einen viel schnelleren Überblick.Aber das ist natürlich auch ein wenig Geschmackssache, wie gesagt helfen mir persönlich Typangaben an vielen Orten.
-
audacia schrieb:
...Kompiliert wird eine generische Klasse erst, wenn sie mit einem konkreten Typen instantiiert wird. Geparst hingegen wird sie schon untypisiert. In den Moduldateien (bei Delphi sind das *.dcu-Dateien) wird dann der Parse-Tree abgelegt, in den bei der Instantiierung nur noch die tatsächlichen Typen injiziert werden müssen....Typrestriktionen...
Danke für die Info.
Jetzt bin ich schlauer.
Gruß,
Simon2.
-
audacia schrieb:
Vorweg: Kompiliert wird eine generische Klasse erst, wenn sie mit einem konkreten Typen instantiiert wird. Geparst hingegen wird sie schon untypisiert. In den Moduldateien (bei Delphi sind das *.dcu-Dateien) wird dann der Parse-Tree abgelegt, in den bei der Instantiierung nur noch die tatsächlichen Typen injiziert werden müssen.
Wobei man auch sagen muss das beide Generics auf der einen, Templates auf der anderen Seite unterschiedliche Vorteile haben.
Generics sind sowohl in .Net als auch in Java (ich weiß nicht wie es im klassischen Delphi ist) in anderen Belangen wiederum eingeschränkter als C++ Templates (z.B. fehlen die Mechanismen der Templatemetaprogrammierung). Die Vorteile sollte man alleine an deinem Abschnitt ablesen können.
cu André
-
asc schrieb:
Wobei man auch sagen muss das beide Generics auf der einen, Templates auf der anderen Seite unterschiedliche Vorteile haben.
Ich glaube fast, das erwähnt zu haben
Ich spreche mich ja auch nicht gegen Templates aus. Aber für Dinge wie Container sind Generics schlicht besser geeignet.
-
audacia schrieb:
Ich spreche mich ja auch nicht gegen Templates aus. Aber für Dinge wie Container sind Generics schlicht besser geeignet.
Und ich bin der Meinung wenn man alles was grundsätzlich positiv wäre noch zusätzlich in den C++ Standard einbaut kann man gleich eine Diplomarbeit für die Verwendung der Sprache verlangen. In sofern hätte ich aber auch nichts gegen eine neue Sprachversion (wegschmeißen von Altlasten, von mir aus kann man innerhalb eines speziellen Codetags dann explizit für alte Projekte die Sprache auf das bisherige C++ umschalten).
-
asc schrieb:
Und ich bin der Meinung wenn man alles was grundsätzlich positiv wäre noch zusätzlich in den C++ Standard einbaut kann man gleich eine Diplomarbeit für die Verwendung der Sprache verlangen.
Dazu reichts eigentlich auch jetzt schon
asc schrieb:
In sofern hätte ich aber auch nichts gegen eine neue Sprachversion (wegschmeißen von Altlasten, von mir aus kann man innerhalb eines speziellen Codetags dann explizit für alte Projekte die Sprache auf das bisherige C++ umschalten).
Interessanterweise ist das etwa, was CodeGear mit Delphi plant:
The Future of the Delphi Compiler
-
audacia schrieb:
asc schrieb:
Wobei man auch sagen muss das beide Generics auf der einen, Templates auf der anderen Seite unterschiedliche Vorteile haben.
Ich glaube fast, das erwähnt zu haben
Ich spreche mich ja auch nicht gegen Templates aus. Aber für Dinge wie Container sind Generics schlicht besser geeignet.
C++/CLI hat bekanntlich beides (und ja, die Generics sind intern exakt gleich wie in C# oder Delphi, was auch ein Argument für sie ist, da man sie dann zwischen den Sprachen ohne Probleme hin und herschieben kann). Und beim Programmieren damit muss ich einfach sagen, dass es ziemlich auf die Situation ankommt, ob ich nun Generics oder Templates verwende. Gewisse Dinge lassen sich mit Generics schlicht nicht darstellen, etwa eine Collection von Zeigern auf PODs oder Funktionen. Oder eine Collection fixer Länge, da kannst du im Prinzip nur Templates benutzen, weil Generics nur Typen als Argumente nehmen. Somit ergeben sich dann immer wieder Fehler, welche einen Template-Benutzer überaschen mögen:
template<typename T> ref struct Foo { typedef T export_t; }; generic<typename T> ref struct Bar { typedef T export_t; }; int main() { Foo<int>::export_t A; // Wunderbar. Bar<int>::export_t B; // Wunderbar. Foo<int *>::export_t A_ptr; // Wunderbar. Bar<int *>::export_t B_ptr; // BOOM. Das darf man nicht. return 0; }
In der Praxis läuft es nun meistens darauf hinaus, dass ich Generics benutze, wo ich kann und die Templates, wo ich muss. Das ist recht komfortabel, sobald man sich damit zurecht gefunden hat. Ich sehe die Generics und Templates inzwischen schon fast als sich ergänzende Techniken. Aber gerade einfacher gemacht hat es die Sprache bestimmt nicht, insofern muss ich der Sache mit den Diplomarbeiten zustimmen.
MfG