constexpr



  • Ich habe schon einige Artikel zum Schlüsselwort constexpr gelesen, aber mir sind folgende zwei Sachen immer noch nicht klar:

    1.) Funktionsweise & Optimierung
    Mein Verständis ist, dass constexpr dafür sorgt, dass zur Compilezeit der Wert einer Funktion oder eines Parameters berechnet wird. Beispiel:

    constexpr int sqr1(int arg)
    { return arg*arg; }
    
    int main()
    {
      int X = 2;
    
       int myVal = sqr1(X); // 4
      return 0;
    }
    

    Wo ist der Gewinn? Ich dachte der Compiler hätte bei aktivierten Optimierungen eh erkannt, dass X immer 2 ist und dann automatisch myVal zur Compilezeit ausgerechnet?

    2.) Wann constexpr nutzen?
    Wann soll ich jetzt constexpr benutzen? Aus dem Heise-Artikel:

    Der Autor erwartet, constexpr in seinen Projekten ähnlich oft zu verwenden wie das klassische const: nämlich überall dort, wo es zulässig ist.
    

    (Siehe: http://www.heise.de/developer/artikel/C-11-auch-ein-Stimmungsbild-1345406.html?artikelseite=2)
    Also jedes const zu constexpr?



  • c++14 schrieb:

    Wo ist der Gewinn? Ich dachte der Compiler hätte bei aktivierten Optimierungen eh erkannt, dass X immer 2 ist und dann automatisch myVal zur Compilezeit ausgerechnet?

    Das wird wohl auch so sein, aber das constexpr stellt sicher, dass die Funktion so aufgebaut ist, das dies gewährleistet ist. constexpr heißt nicht: "Ich erzähle dem Compiler, dass die Funktion zur Compilezeit ausgewertet werden kann" sonder "Compiler stell bitte sicher, dass die Funktion zur Compilezeit ausgewertet werden kann und gib mir ansonsten eine Fehlermeldung". Ähnlich verhält es sich bei dem Schlüsselwort inline und const für den this-Pointer.



  • c++14 schrieb:

    Also jedes const zu constexpr?

    Ich lehne mich mal weit aus dem Fenster und sage: Dort wo es möglich ist sollte es nie schaden und hin und wieder nützen. Das es möglich ist, ist aber gar nicht so oft der Fall. Damit eine Variable constexpr sein kann, muss sie zum einen constexpr-konstruierbar sein und die Cunstructor-Argumente müssen ebenfalls constexpr sein.


  • Mod

    "Compiler stell bitte sicher, dass die Funktion zur Compilezeit ausgewertet werden kann und gib mir ansonsten eine Fehlermeldung".

    Nein. Eine constexpr Funktion kann auch zur Laufzeit aufgerufen werden, und die Argumente müssen nicht konstant sein.

    Bei constexpr Variablen ist das anders.



  • Arcoth schrieb:

    Nein. Eine constexpr Funktion kann auch zur Laufzeit aufgerufen werden, und die Argumente müssen nicht konstant sein.

    Wo widerspricht das meiner Aussage? Ich schrieb doch nur, das es möglich sein muss. Dazu müssen die Argumente natürlich auch constexpr sein, das hätte ich vielleicht noch sagen sollen.


  • Mod

    Achso, du meintest dass irgendein Aufruf möglich ist, der zur Compilezeit ausgewertet werden kann? Hab' ich missverstanden, sorry. Dachte du redest von einem konkreten Funktionsaufruf.



  • Noch mal etwas präziser: Das Ergebnis einer constexpr Funktion kann an Stellen verwendet werden an denen nur Compilezeit-Konstanten erlaubt sind falls die Funktionsargumente ebenfalls constexpr sind. Dazu muss die Funktion bestimmte Bedingungen erfüllen deren Einhalten vom Compiler sichergestellt wird.



  • Mit Hilfe von constexpr Methoden ist es z.B. meines Wissens nach erstmals möglich, einen Datentyp zu definieren der alles kann, was ein Integer auch kann, ohne sich implizit in diesen oder von diesen konvertieren zu lassen. Etwas, was ich hin und wieder mal gebrauchen kann z.B. für ID-Typen. Allerdings muss man dafür immer noch jede Menge Boilerplate-Code schreiben.



  • @c++14
    Es geht (auch) darum, dass du constexpr Funktionen und Variablen auch an Stellen verwenden kannst, wo der C++ Standard einen konstanten Ausdruck verlangt.

    Also z.B.

    template <int N>
    class Foo {...};
    
    inline int sqr1(int x) { return x * x; }
    
    void fun1()
    {
        const int a = 123;
        const int b = sqr1(a);
        //Foo<b> f; // geht nicht
    }
    
    constexpr inline int sqr2(int x) { return x * x; }
    
    void fun2()
    {
        constexpr int a = 123;
        constexpr int b = sqr2(a);
        Foo<b> f; // GEHT
    }
    

    Genau so bei Array-Grössen oder innerhalb von Static Asserts.

    Und natürlich zusätzlich, was ja schon geschrieben wurde: mit constexpr kannst du sicher sein dass der Compiler die Funktion zur Compilezeit berechnen kann (vorausgesetzt alle Parameter sind konstante Ausdrücke).
    Ohne constexpr kannst du das nur vermuten.

    EDIT: OK, das hat ja TNA auch schon geschrieben. :blind:
    Naja Wurst, jetzt haste noch ein schönes Beispiel dazu 🙂 /EDIT

    @all
    Ist laut Standard vorgeschrieben dass constexpr Funktionen mit konstanten Parametern (passend initialisierte static const, constexpr und Literals) auch immer compile-time ausgewertet werden? Oder darf der Compiler hier "faul" sein und sagen "nö, an der Stelle brauch ich das Ergebnis nicht unbedingt zur compile-time, das lass ich dann zur Laufzeit berechnen"?
    Also

    int main()
    {
        // sqr2(123 + sizeof(long)) Berechnung zwingend compile-time oder nur "hoffentlich meistens" compile-time?
        std::cout << sqr2(123 + sizeof(long)) << "\n";
    }
    


  • Warum ist eigentlich nicht alles implizit constexpr (falls möglich)? Ich finde überall constexpr hinzuschreiben sehr mühselig und es bläht den Code künstlich auf -- es wird unleserlich.

    Deshalb schreibe ich nie constexpr, ausser ich brauche es nachweislich.



  • implonstex schrieb:

    Warum ist eigentlich nicht alles implizit constexpr (falls möglich)? Ich finde überall constexpr hinzuschreiben sehr mühselig und es bläht den Code künstlich auf -- es wird unleserlich.

    Deshalb schreibe ich nie constexpr, ausser ich brauche es nachweislich.

    Kompatibilitaet mit bestehendem Code. Ich schreibe auch fast nie constexpr hin, denn meistens muesste der Compiler fuer Optimierungen intelligent genug sein und fuer das bisschen, was ich vielleicht noch irgendwie rausholen koennte, opfer ich weder Zeit, noch mache ich meinen Code unleserlicher.



  • implonstex schrieb:

    Warum ist eigentlich nicht alles implizit constexpr (falls möglich)? Ich finde überall constexpr hinzuschreiben sehr mühselig und es bläht den Code künstlich auf -- es wird unleserlich.

    Deshalb schreibe ich nie constexpr, ausser ich brauche es nachweislich.

    Ich glaube das geht aus der bisherigen Diskussion schon hervor: "constexpr falls möglich" interpretiere ich so, dass überall dort, wo der Compiler während der Konstantenfaltung (Optimierung) feststellt, dass der Audruck konstant ist, ein constexpr angenommen wird. Das Problem ist, dass diese Entscheidung abhängig vom Compiler und den gewählten Optimierungseinstellungen ist.
    Wenn mein Code an einer bestimmten Stelle einen compile time-konstanten Audruck erfordert, z.B. als Template-Argument, würde das bedeuten, dass dieser Code nur dann erfolgreich kompiliert werden kann, wenn Compiler- und Optimierungseinstellungen passend sind, und das ist nicht gerade praktisch im Einsatz :D. constexpr garantiert hingegen einen solchen konstanen Audruck, und mein Code kompiliert immer.

    Finnegan



  • @Finnegan
    Ich denke du hast das falsch verstanden.

    Der Compiler guckt zwar selbst was er wie optimieren kann, was natürlich das vor-berechnen von konstanten Ausdrücken einschliesst, auch wenn diese nicht explizit als constexpr deklariert sind.

    Er erlaubt dir aber in keinem Fall, an Stellen wo ein konstanter Ausdruck erwartet wird, etwas zu verwenden, was nicht per Definition ein compile-time konstanter Ausdruck ist.

    Und "constexpr falls möglich" bedeutet: der Programmierer soll quasi überall wo es geht (=möglich ist) constexpr dranschreiben.



  • Ich denke es ist in etwa so wie const-Methoden. Man könnte auch argumentieren,dass der Compiler selber erkennen könnte, das die Methode das Objekt nicht verändert.



  • Ich fände das sogar witzig. Also als opt-in Warning ala The method MyClass::MyMethod could be const. Consider changing the signature. 🕶
    Same-4-constexpr.



  • Wobei eine Sache noch gar nicht erwähnt wurde: constexpr funktionen sind gleichzeitig "inline" d.h. die Definition muss überall dort verfügbar sein, wo die Funktion verwendet wird, was aber ohne "constexpe" oder "inline" gar nicht möglich wäre, da Mehrfachdefinitionen im Allgemeinen verboten sind. constexpr Funktionen decken damit auch ein Gebiete ab was vorher Macros vorbehalten war oder höchstens noch Templates mit sehr üblen kaum lesbaren Tricks.


  • Mod

    "Compiler stell bitte sicher, dass die Funktion zur Compilezeit ausgewertet werden kann und gib mir ansonsten eine Fehlermeldung".

    Tja, das Proble ist, dass der Compiler dir keine Fehlermeldung geben muss:

    [dcl.constexpr]/5 schrieb:

    For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no diagnostic required.


Log in to reply