constexpr oder nicht?



  • Hallo alle,

    bei folgendem Beispiel würde ich gerne wissen ob das
    alles zur Compiletime berechnet wird:

    //g++ -Wpedantic -Wall -Wextra -std=c++17 mauerverband.cpp -o mauerverband
    #include <cstdlib>
    #include <iostream>
    
    using namespace std;
    
    typedef long length_t;
    const length_t ein_mm = 100;
    
    constexpr length_t operator "" _mm (unsigned long long value) {
      return value * ein_mm;
    }
    constexpr length_t operator "" _mm (long double value) {
      return value * ein_mm;
    }
    
    constexpr length_t operator "" _m (unsigned long long value) {
      return value * 1000 * ein_mm;
    }
    constexpr length_t operator "" _m (long double value) {
      return value * 1000 * ein_mm;
    }
    
    // Achtel-Meter. ein "Kopf"
    constexpr length_t operator "" _am (unsigned long long value) {
      return value * 125_mm;
    }
    
    // Schichthöhe Normalformat (aka Ziegel) mit Lagerfuge
    const length_t NF_hoehe = 83.3_mm; // 3 Schichten NF = 2_am = 25cm
    const length_t DIFF_2AM_3NF = 2_am - 3*NF_hoehe; // Differenz zw. 2_am und 3*NF
    
    // differenz alle 3 Schichten aufaddieren
    constexpr length_t operator "" _schicht (unsigned long long s) {
      return (s/3)*3*NF_hoehe + (s/3)*DIFF_2AM_3NF + (s%3)*NF_hoehe;
    }
    // Erst ab c++14 constexpr?
    // the function body must be either deleted or defaulted or contain any statements except:
    //  - ...
    //  - a definition of a variable for which no initialization is performed.
    // http://en.cppreference.com/w/cpp/language/constexpr
    constexpr length_t operator "" _schicht_v2 (unsigned long long s) {
      const unsigned long long n = s/3;  // Erlaubt für constexpr?
      return n*3*NF_hoehe + n*DIFF_2AM_3NF + (s%3)*NF_hoehe;
    }
    
    constexpr length_t nf_schicht(int a)
    {
      return operator"" _schicht(a);
    }
    
    constexpr double meter(length_t m)
    {
      return 1. * m / 100000;
    }
    
    int main()
    {
      // Compiletime-Berechnung (imho)
      cout << "test 12am = " << 1.5_m << " 1/100mm = "<< meter(1.5_m) << "m\n";
      cout << "test  8am = " << 8_am << " 1/100mm = "<< meter(8_am) << "m\n";
      cout << "1 Schicht   NF = " << 1_schicht << " 1/100mm = " << meter(1_schicht) << "m\n";
      cout << "2 Schichten NF = " << operator"" _schicht(2) << " 1/100mm\n";
    
      // und hier auch? operator""_schicht_v2 erst ab c++14 ?
      cout << "3 Schichten NF = " << 3_schicht_v2 << " 1/100mm\n";
      cout << "4 Schichten NF = " << nf_schicht(4) << " 1/100mm\n";
      const length_t t = 5;
      cout << t << " Schichten NF = " << nf_schicht(t) << " 1/100mm = "<< meter(nf_schicht(t)) << "m\n";
    
      // hier nicht wegen Variabel
      length_t t2 = 6;
      cout << t2 << " Schichten NF = " << nf_schicht(t2) << " 1/100mm = "<< meter(nf_schicht(t2)) << "m\n";
    
      return EXIT_SUCCESS;
    }
    

    Ausgabe:

    test 12am = 150000 1/100mm = 1.5m
    test  8am = 100000 1/100mm = 1m
    1 Schicht   NF = 8330 1/100mm = 0.0833m
    2 Schichten NF = 16660 1/100mm
    3 Schichten NF = 25000 1/100mm
    4 Schichten NF = 33330 1/100mm
    5 Schichten NF = 41660 1/100mm = 0.4166m
    6 Schichten NF = 50000 1/100mm = 0.5m
    

    bei .. << 3_schicht_v2 << ... und ... << nf_schicht(4) << ... bin ich mir nicht sicher.
    Und gibt es irgendeine -W-option womit man sich das anzeigen lassen kann?

    Grüße



  • Warum definierst du die ganzen globalen Variablen als const und nicht constexpr? Da du diese Konstanten in fast jeder Funktion verwendest, werden diese Funktionen, würde ich sagen, nicht zur Compiletime ausgerechnet. Könnte davon abhängen, ob Optimierungen eingeschaltet sind.
    Kompiliert das denn überhaupt? Ich dachte, dass constexpr vor einer Funktion zwingend eine Berechnung zur Compiletime erfordert und deswegen keine variablen Parameter haben darf.



  • cccc schrieb:

    Warum definierst du die ganzen globalen Variablen als const und nicht constexpr? Da du diese Konstanten in fast jeder Funktion verwendest, werden diese Funktionen, würde ich sagen, nicht zur Compiletime ausgerechnet. Könnte davon abhängen, ob Optimierungen eingeschaltet sind.
    Kompiliert das denn überhaupt? Ich dachte, dass constexpr vor einer Funktion zwingend eine Berechnung zur Compiletime erfordert und deswegen keine variablen Parameter haben darf.

    Naja, in dem fall dachte ich bei Konstanten ist es egal ob const oder constexpr. Ist auch nur ein Minimalbeispiel.

    constexpr-Funktionen müssen (soweit ich weiß) nicht wirklich constexpr sein.
    Sonnst könnte man sie ja nicht mit einer Variablen aufrufen. Dann werden sie natürlich nicht während der Übersetzung berechnet...

    Ich dachte es wird bei der Übersetzung berechnet wenn alle Werte zur compiletime bekannt sind.

    Und ja, wird übersetzt, siehe Ausgabe. Der g++-Aufruf ist im Quellcode ganz oben als Kommentar...


  • Mod

    Bekanntlich sind Implementierungen nicht verpflichtet, Ausdrücke zur Compilezeit auszuwerten, die nicht auch zur Compilezeit zwingend gebraucht werden. Man kann davon ausgehen, dass es geschieht, d.h. aus Optimierungsgründen sollte man sich da keine Gedanken machen. Wenn die Semantik deines Programs davon abhängt, solltest du eine constexpr Variable entsprechend initialisieren und diese dann stattdessen verwenden.



  • Ok,ich habe mal die Zeilen 8, 32 und 33 auf constexpr geändert, Ergebnis ist gleich.

    mit -O2 kommen alle Zahlen in der Reihenfolge der Ausgabe im binary vor (150000, 100000, 8330, 16660, ...).
    ohne nur 8330 und 25000 aus den Zeilen 32 u. 33 (83.3_mm und 3*NF_hoehe 2_am), der Rest wird zur Laufzeit berechnet.

    Ach ja, mit g++ (SUSE Linux) 7.2.1 20171020 [gcc-7-branch revision 253932]

    Grüße

    Edit: Zeilenzahlen korrigiert 🙄

    Edit2: ...oder einfach mit static_assert testen. War schon spät 😃



  • Zusammenfassend:

    Ab c++14 können alle Funktionen während der Übersetzung berechnet werden. Bei c++11 meckert der g++ bei

    constexpr length_t operator "" _schicht_v2 (unsigned long long s) {
      const unsigned long long n = s/3;  // Erlaubt für constexpr?
      return n*3*NF_hoehe + n*DIFF_2AM_3NF + (s%3)*NF_hoehe;
    }
    
    ...und...
    
    static_assert (1_m == 12_schicht_v2, "12_schicht_v2 nicht compiletime berechenbar");
    

    rum. Ist ja auch erst ab c+14 erlaubt.

    #include <cstdlib>
    #include <iostream>
    
    using namespace std;
    
    typedef long length_t;
    constexpr length_t ein_mm = 100;
    
    constexpr length_t operator "" _mm (unsigned long long value) {
      return value * ein_mm;
    }
    constexpr length_t operator "" _mm (long double value) {
      return value * ein_mm;
    }
    
    constexpr length_t operator "" _m (unsigned long long value) {
      return value * 1000 * ein_mm;
    }
    constexpr length_t operator "" _m (long double value) {
      return value * 1000 * ein_mm;
    }
    
    // Achtel-Meter. ein "Kopf"
    constexpr length_t operator "" _am (unsigned long long value) {
      return value * 125_mm;
    }
    
    // Schichthöhe Normalformat (aka Ziegel) mit Lagerfuge
    constexpr length_t NF_hoehe = 83.3_mm; // 3 Schichten NF = 2_am = 25cm
    constexpr length_t DIFF_2AM_3NF = 2_am - 3*NF_hoehe; // Differenz zw. 2_am und 3*NF
    
    // differenz alle 3 Schichten aufaddieren
    constexpr length_t operator "" _schicht (unsigned long long s) {
      return (s/3)*3*NF_hoehe + (s/3)*DIFF_2AM_3NF + (s%3)*NF_hoehe;
    }
    // Erst ab c++14 constexpr?
    // the function body must be either deleted or defaulted or contain any statements except:
    //  - ...
    //  - a definition of a variable for which no initialization is performed.
    // http://en.cppreference.com/w/cpp/language/constexpr
    constexpr length_t operator "" _schicht_v2 (unsigned long long s) {
      const unsigned long long n = s/3;  // Erlaubt für constexpr?
      return n*3*NF_hoehe + n*DIFF_2AM_3NF + (s%3)*NF_hoehe;
    }
    
    constexpr length_t nf_schicht(int a)
    {
      return operator"" _schicht(a);
    }
    
    constexpr double meter(length_t m)
    {
      return 1. * m / 100000;
    }
    
    int main()
    {
      // um zu testen ob constexpr-Funktionen compiletime berechnet werden einfach
      // eine constexpr-Konstante mit initialisieren. Wenn nicht möglich dann fehler.
      // Oder mit assert.
      static_assert (1_m == ein_mm * 1000, "1_m nicht compiletime berechenbar");
      static_assert (1_m == 8_am, "8_am nicht compiletime berechenbar");
      static_assert (1_m == 12_schicht, "12_schicht nicht compiletime berechenbar");
      static_assert (1_m == 12_schicht_v2, "12_schicht_v2 nicht compiletime berechenbar");
      static_assert (1_m == ein_mm * 1000, "1_m nicht compiletime berechenbar");
      static_assert (1_m == nf_schicht(12), "8_am nicht compiletime berechenbar");
      constexpr length_t ct1 = nf_schicht(4);
    
      // Compiletime-Berechnung (imho)
      cout << "test 12am = " << 1.5_m << " 1/100mm = "<< meter(1.5_m) << "m\n";
      cout << "test  8am = " << 8_am << " 1/100mm = "<< meter(8_am) << "m\n";
      cout << "1 Schicht   NF = " << 1_schicht << " 1/100mm = " << meter(1_schicht) << "m\n";
      cout << "2 Schichten NF = " << operator"" _schicht(2) << " 1/100mm\n";
    
      // und hier auch? operator""_schicht_v2 erst ab c++14 ?
      cout << "3 Schichten NF = " << 3_schicht_v2 << " 1/100mm\n";
      cout << "4 Schichten NF = " << ct1 << " 1/100mm\n";
      const length_t t = 5;
      cout << t << " Schichten NF = " << nf_schicht(t) << " 1/100mm = "<< meter(nf_schicht(t)) << "m\n";
    
      // hier nicht wegen Variabel
      length_t t2 = 6;
      cout << t2 << " Schichten NF = " << nf_schicht(t2) << " 1/100mm = "<< meter(nf_schicht(t2)) << "m\n";
    
      return EXIT_SUCCESS;
    }
    


  • Für solche Fragen finde ich godbolt toll. Vor allem weil du schnell man den Compiler und/oder die Optimierungsflags einstellen kannst und sofort Auswirkungen auf das Ergebnis siehst. In deinem Fall: https://godbolt.org/g/ao7zvL


Anmelden zum Antworten