Type inference - gutes C++0x-Feature?



  • 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 konvertierbaren PolarVector2f zurückgibt. Der unterstützt nicht alle Operationen von sf::Vector2f .

    Versteh mich nicht falsch, ich finde auto und decltype sehr praktisch. Aber nur weil man die Möglichkeit hat, muss man sie nicht gleich überall unbedacht einsetzen.



  • Nexus schrieb:

    Aus einem schon etwas älteren Spiel von mir

    Da sehe ich durch auto keinen Informationsverlust.
    Die Variablen sind eh ewig weit weg von ihrer Verwendung deklariert.

    Hast du vielleicht noch ein anderes?

    PS:
    Nicht falsch verstehen, aber in F# zB wird alles über Type Inference gemacht und das fühlt sich absolut natürlich an. Deshalb tue ich mir gerade schwer negativ Beispiele zu aktzeptieren...



  • Shade Of Mine schrieb:

    Nicht falsch verstehen, aber in F# zB wird alles über Type Inference gemacht und das fühlt sich absolut natürlich an. Deshalb tue ich mir gerade schwer negativ Beispiele zu aktzeptieren...

    Was haben den alle Sprachen mit Type Inference gemeinsam? Referenzsemantik?



  • Shade Of Mine schrieb:

    Die Variablen sind eh ewig weit weg von ihrer Verwendung deklariert.

    Findest du? Man sieht sie immerhin direkt, wenn man den verwendenden Code anschaut. Ich finde das Beispiel ohne Typinferenz wirklich übersichtlicher.

    Shade Of Mine schrieb:

    Hast du vielleicht noch ein anderes?

    Schon, aber ob es besser ist...? Worin läge hier der Vorteil von auto ?

    bool aboveTile = (obj.GetBottom() - vy - std::max(vx, 0.f) - tolerance <= tileY * Tile::TileSize);
    

    Zeus schrieb:

    Was haben den alle Sprachen mit Type Inference gemeinsam? Referenzsemantik?

    Nein, C++ hat ja Wertsemantik. Was hat das überhaupt mit Typinferenz zu tun?



  • Ich sehe, was Lesbarkeit angeht, zwischen (Beispieltyp aus der Luft gegriffen)

    std::vector<std::pair<std::vector<foo*,std::string>>*> *bar = obscure_function();
    

    und

    ObscureTypedef *bar = obscure_function();
    

    und

    auto bar = obscure_function();
    

    überhaupt keine Unterschiede. Hilfreich zum Verstehen des Codes ist keins von denen dreien - aber wenn man mal einen solchen aus der Reihe tanzenden Typen hat, kommt man einfach nicht drum rum.

    Schwieriger wirds dann schon, wenn ich den Code nicht nur verstehen soll, sondern was ändern und direkt mit dem Typen arbeiten muss:

    auto bar = obscure_funciton();
    
    //hmm... mal nachdenken
    bar[i].first->DoSomething(); //mist, compilerfehler
    
    //hmm... vll. doch eher so?
    (*bar)[i].first->DoSomething(); //hm, passt auch nich
    

    und so weiter.

    Da würde ich die ursprüngliche Fassung

    std::vector<std::pair<std::vector<foo*,std::string>>*> *bar = obscure_function();
    

    schon bevorzugen.

    Wobei man auch sagen muss, dass ich selbst hier auto bevorzugen würde (aufgrund von Schreibfaulheit), falls Visual Assist X und Konsorten auto endlich mitparsen würden.

    Oder noch schöner: Wenn man einfach nen Button klickt (oder ne Tastenkombination drückt) und es wird ne schöne Textersetzung losgefahren, die mir den tatsächlichen Typen hinschreibt.

    Auf alle Fälle sind das aber eher Unangenehmheiten beim Tippen und Kompilieren (im schlimmsten Fall 3 oder 4 mal kompilieren anfangen bis man die Syntax richtig hat). Ich sehe da recht wenige Möglichkeiten, übel unsichtbare Bugs zu erzeugen. Was Codeverstehen angeht, nehmen sich alle 3 Beispiele nicht viel, denke ich.

    Ob der Schreibaufwand, den man sich durch auto zunächst spart, größer ist, als der, der dann entsteht wenn man später wieder zum Code zurückkehren muss, wird man wohl nur mit Erfahrung mit dem Wort rausfinden können.

    Ansonsten, fürs durchiterieren von Ranges ist auto immer gut.


Anmelden zum Antworten