Type inference - gutes C++0x-Feature?
-
Hallo,
nachdem gerade ein anderer Thread nach Sprachmitteln fragt, die ihr nie benutzt, frage ich mich wie ihr zu die neue type inference
auto
findet.
Wie häufig werdet ihr sie nutzen?Also ich persönlich finde, dass sie die Lesbarkeit des Programms ruiniert und ein Zeichen ist, dass ein zusätzlicher typedef wahrscheinlich die bessere Wahl wäre. Natürlich verringert es die Schreibarbeit, aber das sollte nicht auf Kosten der Lesbarkeit geschehen.
Wie seht ihr das?
Für mich ein klares: I don't like
-
Es wird, wie jedes neue Spielzeug, viel missbraucht werden.
Es gibt aber IMO einige Dinge, wo ich die Verwendung vonauto
für sinnvoll und gut halte.
Beispielsweise sehe ich keinen besonderen Vorteil darin, immerstd::some_container<SomeType>::const_iterator
etc. zu schreiben.Und es gibt auch Dinge, die man ohne
auto
gar nicht, nur sehr umständlich oder auf Kosten der Performance machen kann. z.B. einen vonboost::bind
erzeugten Funktor in einer lokalen Variable ablegen. Oder einen über eine Lambda-Expression erzeugten Funktor.Wer eine Sprache will, die so wenig Features wie möglich hat die man misbrauchen könnte, der ist mit Java sicherlich besser bedient als mit C++. Das Problem ist IMO nicht die Sprache, sondern der Programmierer.
-
hustbaer schrieb:
Das Problem ist IMO nicht die Sprache, sondern der Programmierer.
-
Ich würde jetzt auch nicht für jede Deklaration
auto
verwenden, aber bei komplexeren Ausdrücken, wo der exakte Typ nicht relevant ist, kann Typinferenz schon praktisch sein. Aber wenn es übersichtlicher mit ausgeschriebenem Typ ist, kann man an der Stelle ja auchauto
verzichten.Mich stören da andere Dinge an C++0x um einiges mehr, allen voran die "einheitliche" Initialisierung... Ich fürchte, das wird komplett nach hinten losgehen.
-
Nexus schrieb:
Mich stören da andere Dinge an C++0x um einiges mehr, allen voran die "einheitliche" Initialisierung... Ich fürchte, das wird komplett nach hinten losgehen.
Sagt mir nichts, hättest du ein Link?
-
Umfrager schrieb:
Für mich ein klares: I don't like
Es gibt statisch typisierte Sprachen die fast komplett mit Typinferenz arbeiten. Ich finde das toll, da der Typ in vielen idiomatischen Kontexten (zum Beispiel for schleifen mit tieratoren) keine neuen Informationen liefert.
-
ääääääääää schrieb:
Sagt mir nichts, hättest du ein Link?
Grundsätzlich sollen folgende Schreibweisen zur Initialisierung möglich sein:
int a = 4; int a(4); int a = {4}; int a {4};
Wird auch sehr treffend "einheitlich" genannt. Eine ausführliche Begründung, warum ich das für unglücklich halte, findest du hier.
-
In welchen Situationen wäre auto denn nicht gut?
Der genaue Typ einer Variablen ist mir eigentlich so gut wie immer ziemlich egal. Typen braucht nur der Compiler. Ich brauch keine. Ich weiss bei Templates ja auch nicht welchen Typ ich habe.
-
auto ist enorm gut.
Daß die Kenntnis der Typen immer nötig ist, um ein Programm zu verstehen, ist nämlich total falsch. Mit auto kann ich viel Rauschen loswerden.Darüber, daß man mit auto jeden Fehler einzeln machen kann, den man zuvor nur im Azdruck schaffte, würde ich mir mal keine Sorgen machen.
auto einDrittel=1/3;
-
Shade Of Mine schrieb:
In welchen Situationen wäre auto denn nicht gut?
In denen, wo etwas anderes zurückgegeben wird, als abgespeichert werden soll. Zum Beispiel bei Expression Templates oder generell Funktionen, die statt des Objekts ein anderes Objekt zurückgeben, das implizit in ersteres konvertierbar ist.
Shade Of Mine schrieb:
Der genaue Typ einer Variablen ist mir eigentlich so gut wie immer ziemlich egal.
Nur sehr begrenzt. Man muss immer noch genügend über den Typen wissen, um mit seinen Objekten vernünftig arbeiten zu können. Was weniger wichtig ist, sind Implementierungsdetails und kleine Unterschiede (wie z.B. bei Iteratoren). Doch manchmal macht es den Code übersichtlicher, wenn der Typ dasteht.
Shade Of Mine schrieb:
Ich weiss bei Templates ja auch nicht welchen Typ ich habe.
Ja, nur abstrahierst du bei Templates explizit vom Typen und arbeitest generisch, während du nach lokalen Deklarationen meist auf typ-spezifische Funktionalität zugreifst.
-
Ich benutze das auto Keyword im Moment nur bei Container Iteratoren, denn das volle Ausschreiben des Iteratortyps is einfach nur hässlich und jedes Mal einen typdef anlegen ist entweder nervig oder unpraktisch (wenn man den Iterator nur einmal braucht).
Zumindest für diesen Zweck find ich das Feature klasse!
-
Nexus schrieb:
Shade Of Mine schrieb:
In welchen Situationen wäre auto denn nicht gut?
In denen, wo etwas anderes zurückgegeben wird, als abgespeichert werden soll. Zum Beispiel bei Expression Templates oder generell Funktionen, die statt des Objekts ein anderes Objekt zurückgeben, das implizit in ersteres konvertierbar ist.
Müsste man mal exzessiv testen ob das wirklich ein Problem ist. Dazu fehlt mir jetzt die Erfahrung.
Shade Of Mine schrieb:
Der genaue Typ einer Variablen ist mir eigentlich so gut wie immer ziemlich egal.
Nur sehr begrenzt. Man muss immer noch genügend über den Typen wissen, um mit seinen Objekten vernünftig arbeiten zu können. Was weniger wichtig ist, sind Implementierungsdetails und kleine Unterschiede (wie z.B. bei Iteratoren). Doch manchmal macht es den Code übersichtlicher, wenn der Typ dasteht.
Gib mal ein Beispiel.
In OCaml/F# und C# verwende ich wo es nur geht Typinferenz und habe noch nie ein Problem gesehen.
Shade Of Mine schrieb:
Ich weiss bei Templates ja auch nicht welchen Typ ich habe.
Ja, nur abstrahierst du bei Templates explizit vom Typen und arbeitest generisch, während du nach lokalen Deklarationen meist auf typ-spezifische Funktionalität zugreifst.
[/quote]
Ich sehe da keinen Unterschied. Wichtig ist doch die Art des Objektes, nicht der Typ. Wenn es ein string ist, dann mache ich einen operator[] darauf, egal ob es ein char*, std::string oder sonstwas ist.In Java macht man es so, dass man sowieso immer:
Fooable f = new Foo();
schreibt. Man hat immer nur die Art des Objektes, nämlich Fooable, und nie den echten Typen. Mit auto spar ich mir sogar das. Wenn ich den Typen wissen will, dann hovere ich den Mauszeiger über die Variable und die IDE sagt mir den Typ.
-
Nexus schrieb:
ääääääääää schrieb:
Sagt mir nichts, hättest du ein Link?
Grundsätzlich sollen folgende Schreibweisen zur Initialisierung möglich sein:
int a = 4; int a(4); int a = {4}; int a {4};
Wird auch sehr treffend "einheitlich" genannt. Eine ausführliche Begründung, warum ich das für unglücklich halte, findest du hier.
Was ist daran einheitlich? Nun, {} war bisher nur für Aggregat-Typen brauchbar. Jetzt kann man es auch für andere Typen benutzen. Außerdem lässt sich {} jetzt auch für temporäre Objekte verwenden, so, wie es () schon vorher konnte. Ich finde das gut so.
Aber: initializer_lists sind mir etwas suspekt. Mir ist nicht ganz klar, was die Lebenszeit des referenzierten Arrays ist. Manchmal so, manchmal so (static, automatic). Ich kann mir nicht ganz vorstellen, wie das implementiert werden soll. Eine Bedingung lässt mich glauben, dass ggf der Heap mit Referenzzählern benutzt werden müsste. Das entspricht aber sicherlich nicht der Idee von initializer_lists. Außerdem sind die referenzierten Elemente *immer* unveränderlich, was das "moven" aus der Liste unmöglich macht.
Bzgl 'auto':
Wie es auch in anderen Sprachen, wo man Typen nicht benennen muss, üblich ist, sollte man sich angewöhnen, aussagekräftigere Variablennamen zu verwenden. Ich finde auto gut.
Allerdings kann man sich damit auch Ärger einhandeln. Ich denke da zB an Expression-Template-Libraries, wo komplizierte Expression-Typen nur als temporäre Objekte existieren sollten. Per auto kann man deren Lebenszeit verlängern und sich ggf baumelnde Zeiger und Referenzen einhandeln.
-
Shade Of Mine schrieb:
Nexus schrieb:
Shade Of Mine schrieb:
In welchen Situationen wäre auto denn nicht gut?
In denen, wo etwas anderes zurückgegeben wird, als abgespeichert werden soll. Zum Beispiel bei Expression Templates oder generell Funktionen, die statt des Objekts ein anderes Objekt zurückgeben, das implizit in ersteres konvertierbar ist.
Müsste man mal exzessiv testen ob das wirklich ein Problem ist. Dazu fehlt mir jetzt die Erfahrung.
Es kann z.B. zu Problemen kommen, wenn in den Expression Templates Referenzen gespeichert sind, die am Ende des Audrucks ungültig werden und man die Lebenszeit des Expression Templates durch eine Variable verlängert.
auto
macht das etwas leichter, da normalerweise niemand den Typ des Expression Templates hinschreiben würde. Z.B. bei boost.proto kann es zu solchen Problemen kommen. Siehe http://www.boost.org/doc/libs/1_47_0/doc/html/proto/users_guide.html#boost_proto.users_guide.intermediate_form.deep_copying_expressions (gleich mit Lösung).
Das ganze wirkt sich dann auch auf abhängige Bibliotheken wie boost.spirit aus, siehe http://boost-spirit.com/home/articles/qi-example/zero-to-60-mph-in-2-seconds/ (in den Kommentaren).
-
ipsec schrieb:
Shade Of Mine schrieb:
Nexus schrieb:
Shade Of Mine schrieb:
In welchen Situationen wäre auto denn nicht gut?
In denen, wo etwas anderes zurückgegeben wird, als abgespeichert werden soll. Zum Beispiel bei Expression Templates oder generell Funktionen, die statt des Objekts ein anderes Objekt zurückgeben, das implizit in ersteres konvertierbar ist.
Müsste man mal exzessiv testen ob das wirklich ein Problem ist. Dazu fehlt mir jetzt die Erfahrung.
Es kann z.B. zu Problemen kommen, wenn in den Expression Templates Referenzen gespeichert sind, die am Ende des Audrucks ungültig werden und man die Lebenszeit des Expression Templates durch eine Variable verlängert.
Guter Punkt. Hier ist auto in der Tat doof.
Das macht die Sache natürlich etwas tricky.
-
Shade Of Mine schrieb:
Das macht die Sache natürlich etwas tricky.
Ich würde auto für expression templates einfach verbieten. Was die da wieder geboostelt haben, findet nicht mein Entzücken.
-
Umfrager schrieb:
...frage ich mich wie ihr zu die neue type inference
auto
findet.
Wie häufig werdet ihr sie nutzen?Also ich persönlich finde, dass sie die Lesbarkeit des Programms ruiniert...
Ich werde es wohl in Zukunft intensiv benutzen, vor allem bei Iteratoren und anderen Templateausdrücken.
Und im Gegensatz zu dir sehe ich eher eine bessere Lesbarkeit gegeben, auch wenn ich anfangs ebenso skeptisch war. Wenn ich mir aktuell einige Zeilen Code in unseren Projekt anschaue, wo alleine dank Templates die Typinformationen schon ewig lang sind, ist auto ein Segen.
-
volkard schrieb:
Shade Of Mine schrieb:
Das macht die Sache natürlich etwas tricky.
Ich würde auto für expression templates einfach verbieten. Was die da wieder geboostelt haben, findet nicht mein Entzücken.
Nur wie tut man das am sinnvollsten? Man müsste gewisse Typen als nicht inferierbar(?) kennzeichnen können. Denn wenn ich es recht bedenke, dann will ich manchmal wissen was der Zieltyp ist - sprich eine return-type Überladung haben. Das geht mit auto ja nicht wirklich.
uU privaten Ctor für diese Typen?
-
Shade Of Mine schrieb:
uU privaten Ctor für diese Typen?
Jup. Privaten CopyCtor.
-
Shade Of Mine schrieb:
Ich sehe da keinen Unterschied. Wichtig ist doch die Art des Objektes, nicht der Typ.
Da stimme ich zu. Nur sieht man halt mit
auto
auch nicht die Art des Types, was vielleicht manchmal verwirrend ist.Shade Of Mine schrieb:
Gib mal ein Beispiel.
Aus einem schon etwas älteren Spiel von mir, gibt sicher bessere Beispiele:
const sf::Vector2f p = GetFrontPosition(data); const sf::Vector2f w = mWaypoints.front().pos; const sf::Vector2f v = UnitVector(mVelocity); const float vAbs = data.GetSpeed(mType); const float phi = ToRadian(data.GetTurningAngle(mType)); const float r = vAbs / phi; sf::Vector2f m; if (CrossProduct(w - p, v).z < 0) m = p + r * PerpendicularVector(v); else m = p - r * PerpendicularVector(v); const float d = r - Length(w - m);
Die Variablennamen sind so kurz, um die Vektorrechnungen übersichtlich zu halten (hat dazu noch Kommentare). Ist auch kein Problem, da man mit einem Blick gleich sieht, ob es sich um einen Vektor oder einen Skalar handelt. Anders sieht es mit Typinferenz aus, hier muss ich mir bei jeder Variable den initialisierenden Funktionsaufruf gut anschauen...
const auto p = GetFrontPosition(data); const auto w = mWaypoints.front().pos; const auto v = UnitVector(mVelocity); const auto vAbs = data.GetSpeed(mType); const auto phi = ToRadian(data.GetTurningAngle(mType)); const auto r = vAbs / phi; auto m = sf::Vector2f(); // ...
Klar ginge das auch, besonders mit langen aussagekräftigen Namen. Nur hätte ich persönlich länger, um den nachfolgenden Code schnell zu verstehen. Ein weiteres Problem entsteht, wenn eine der Funktionen nicht mehr
sf::Vector2f
, sondern z.B. den implizit konvertierbarenPolarVector2f
zurückgibt. Der unterstützt nicht alle Operationen vonsf::Vector2f
.Versteh mich nicht falsch, ich finde
auto
unddecltype
sehr praktisch. Aber nur weil man die Möglichkeit hat, muss man sie nicht gleich überall unbedacht einsetzen.