Frage zu auto und decltype



  • Hallo,

    ich hab mal ne Frage zu auto und decltype , folgendes Beispiel:

    auto test1 = 2147483648;
    auto test2 = 2147483647 + 1;
    
    std::cout << typeid(test1).name() << '\n';                    // outputs: unsigned long
    std::cout << typeid(decltype(2147483648)).name() << '\n';     // outputs: unsigned long
    std::cout << typeid(test2).name() << '\n';                    // outputs: int
    std::cout << typeid(decltype(2147483647 + 1)).name();         // outputs: int
    

    Warum checkt der Compiler nicht dass z.B. test2 nicht in ein int passt und wählt gleich den nächst-besser passenden Datentyp? Hat das irgendeinen Grund?



  • Ich vermute mal, dass auch der Compiler vor(!) einer Rechnung wie "2147483647 + 1" beiden Summanden einen Typ zuordnen muss. Und das ist in diesem Fall jedes mal ein int . und int+int gibt immer noch int .
    Ohne die Zuordnung eines Typs, wäre die Operation '+' gar nicht definiert.



  • Schon, aber könnte er nicht auch gleich noch checken ob dabei ein Overflow entsteht? Denn ein Ausdruck wie int i = 2 + 2; wird ja auch zu int i = 4; optimiert, der Compiler muss die Rechnung also sowieso ausführen, oder? Daher könnte er ja gleich checken ob die Werte zu groß werden und bei Bedarf mit einem "größerem" Datentyp neu anfangen.

    Ich frag mich halt gerade was gegen diese Strategie sprechen würde...



  • Ich denke mal, dass da verschiedene Teile des Compilers involviert sind. Wie du ja sicher weist, besteht ein Compiler aus einem Lexer, einem Parser, einem Codegenerierer und einem Optimizer.

    Das Zusammenfalten von "2+2" nach "4" macht der Optimizer (es sei denn, du baust mit Templates rum; je nachdem). Aber zu dem Zeitpunkt steht natürlich schon der Code drumherum, d.h. der Compiler weiss, dass 2 ein int, weiss das aus dem binären +-Operator ein int rauskommt. Aber welche Zahlen da nun reinkommen, kann er nicht wissen. Das ist für ihn unentscheidbar.

    Umgekehrt, könnte man mit solcher Technik (so sie denn möglich wäre), Programmfehler schon beim Kompilieren finden bzw. verhindern.



  • Das ist keine Frage darüber, wie Compiler aufgebaut sind, sondern wie die Sprache definiert ist. Und der Sprachstandard sagt ganz klar, dass das Programm wohlgeformt ist, kompilieren muss und und decltype(test2)==int, aber auch dass es zur Runtime einen Overflow gibt, was zu UB führt, sobald dieser Code ausgeführt wird.

    Sprich, der Compiler ist gezwungen, das so zu machen.

    Skym0sh0 schrieb:

    Das Zusammenfalten von "2+2" nach "4" macht der Optimizer

    Im GCC wird Constant-Folding schon vom Lexer erledigt.

    Aber welche Zahlen da nun reinkommen, kann er nicht wissen. Das ist für ihn unentscheidbar.

    Aber Warnungen kann er trotzdem generieren?

    @TE: Das Problem stellt sich bei mir nie, weil ich immer Warnungen anschalte und bei so einem Problem warnt jeder Compiler. Oder gibt je nach Einstellung sogar einen Compilerfehler.



  • compilertrauer schrieb:

    Skym0sh0 schrieb:

    Das Zusammenfalten von "2+2" nach "4" macht der Optimizer

    Im GCC wird Constant-Folding schon vom Lexer erledigt.

    Okay, dann ist mein obiger Beitrag komplett unnützt. 🙂

    Echt mies, wenn die Hochschule, wo man studiert, kein Compilerbau anbietet...



  • compilertrauer schrieb:

    Das ist keine Frage darüber, wie Compiler aufgebaut sind, sondern wie die Sprache definiert ist. Und der Sprachstandard sagt ganz klar, dass das Programm wohlgeformt ist, kompilieren muss und und decltype(test2)==int, aber auch dass es zur Runtime einen Overflow gibt, was zu UB führt, sobald dieser Code ausgeführt wird.

    Sprich, der Compiler ist gezwungen, das so zu machen.

    Das ist für mich keine Begründung und ich sehe auch nicht warum der Compiler gezwungen wäre es genau so zu machen. Wenn ich etwas schreibe wie zum Beispiel:

    auto c = 'a';
    std::cout << c; // Displays: a
    

    dann wählt der Compiler auch automatisch den "richtigen" Typ aus, nämlich char. Er könnte an dieser Stelle aber genauso gut int nehmen - mit int wäre es auch wohlgeformt, müsste compilieren und decltype(c) == int würde gelten. Nach deiner Logik wäre der Compiler dann hier ja auch gezwungen int zu nehmen. Aber da für verschiedene Kombinationen auch immer verschiedene (gleich richtige!) Möglichkeiten gibt, kann der Compiler sich ja sowieso nicht darauf verlassen dass das Program "nur" wohlgeformt ist und kompilieren muss.

    So ist das Verhalten halt nicht immer unbedingt das, was man von einem auto keyword erwarten würde. Von so einem keyword sollte man doch meinen dass es automatisch den "richtigsten" Typ bestimmt.

    compilertrauer schrieb:

    Im GCC wird Constant-Folding schon vom Lexer erledigt.

    Dann versteh ich noch weniger warum man sowas nicht macht?

    compilertrauer schrieb:

    @TE: Das Problem stellt sich bei mir nie, weil ich immer Warnungen anschalte und bei so einem Problem warnt jeder Compiler. Oder gibt je nach Einstellung sogar einen Compilerfehler.

    Das Problem ist dass der Compiler hier gar keine Warnung generieren müsste wenn er einfach den richtigen Typ wählen würde, das meine ich.


  • Mod

    Das ist für mich keine Begründung und ich sehe auch nicht warum der Compiler gezwungen wäre es genau so zu machen.

    Natürlich ist das die Begründung, weil der Compiler standardkonform arbeiten soll!

    Er könnte an dieser Stelle aber genauso gut int nehmen

    Nein. Ich frage mich, ob du auch siehst, warum. Kleiner Tipp: Die Ausgabe ist anders.

    Nach deiner Logik wäre der Compiler dann hier ja auch gezwungen int zu nehmen.

    Nein. Nach seiner Logik wäre der Compiler gezwungen char zu nehmen, weil der Typ des Literals char ist und der Typ der Variable dann per template argument deduction deduziert wird.

    Dann versteh ich noch weniger warum man sowas nicht macht?

    Weil C++ standardisiert ist.

    Das Problem ist dass der Compiler hier gar keine Warnung generieren müsste wenn er einfach den richtigen Typ wählen würde

    Er wählt den richtigen Typ! Nach Sprachstandard! Die Typen beider Literale sind int , und daher ist der Typ des ganzen Ausdrucks int .



  • Arcoth schrieb:

    Nein. Ich frage mich, ob du auch siehst, warum. Kleiner Tipp: Die Ausgabe ist anders.

    Ja schon. Aber

    int c = 'a';
    std::cout << c;
    

    wäre genauso wohlgeformt und würde kompilieren. Daher ist das alleine (diese zwei Bedingungen) halt kein ausreichendes Kriterium, der Compiler muss zusätzlich noch schauen welcher Typ am ehesten passt.

    Arcoth schrieb:

    Er wählt den richtigen Typ! Nach Sprachstandard! Die Typen beider Literale sind int , und daher ist der Typ des ganzen Ausdrucks int .

    Ja, also mir ist schon klar dass das (sehr wahrscheinlich) irgendwo im Standard so drin steht. Ich wollte aber auch nicht fragen ob das jetzt so Standardkonform ist oder nicht. Die Frage ist vielmehr warum steht das da so drin und nicht anders (z.b. so wie oben beschreiben, wie es meiner Meinung nach besser wäre). Gibt es einen triftigen Grund dafür (den ich momentan nicht sehe) oder nicht?



  • Das Literal selbst bestimmt schon den Datentypen, d.h. wenn du bspw. ein unsigned int haben willst, dann mußt du 1u schreiben (und dementsprechend für die anderen Datentypen die Suffixe l, ul, f, ... - oder äquivalent dazu die Großbuchstaben U, L, UL, F).
    auto bestimmt also nicht den besten Datentypen, sondern beruht einzig und allein auf der Datentyp-Deduktion.



  • Ja, schade irgendwie, aber trotzdem Danke für die Antworten 🙂

    Vielleicht haben sie das auch extra gemacht damit das Ergebnis von auto nicht davon abhängt ob ein Wert dynamisch ist oder nicht...



  • Nein, es ist ganz einfach. In C++ kann den Typen eines Ausdrucks bestimmen, wenn man die Typen der Teilausdrücke kennt. Nur so ist statische Typsicherheit überhaupt möglich und vor allem konsistent.

    Wenn man jetzt anfangen würde, abhängig von den Werten die Typen zu bestimmen, hätte man plötzlich ein Riesenchaos und einen unnötigen Fallstrick mehr, den man nie erwartet.


Log in to reply