Warum constexpr anstatt erweiterte call Syntax?



  • Hallo,

    ich habe mich in letzter Zeit in constexpr eingearbeitet, was schon ein ziemlich nützliches Feature ist. Allerdings verstehe ich nicht so ganz warum das Konzept hinter constexpr (also Code zur Compile-Zeit auszuwerten) so implementiert ist, wie es ist.

    Das man eine Variable wie z.B. constexpr int i = 0; mit constexpr deklarieren muss, macht ja Sinn. Aber warum auch Funktionen?

    Meiner (aktuellen) Meinung nach führt das zu folgenden Nachteilen:

    • Inflationäre Benutzung des constexpr keywords (weil zu mindest in meinem Code ca. 80% aller Funktionen als constexpr deklariert werden können).
    • Weniger "Macht" beim User: Ob eine constexpr Funktion tatsächlich zur Compile-Zeit ausgewertet wird kann man nur über Umwege wie die Deklaration einer lokalen constexpr Variable erzwingen. Andererseits kann es sein dass ich eine Funktion trotz constexpr zur Laufzeit aufrufen will, der Compiler aber anderer Meinung ist.

    Warum nicht einfach in der Art (Syntax ist natürlich provisorisch):

    void foo() {}
    int bar() { return 1; }
    
    int main()
    {
        foo();    // (1) Standard: Compiler entscheidet
        foo!rt(); // (2) Erweiterte call Syntax: foo wird (garantiert) zur Runtime ausgewertet
        foo!ct(); // (3) foo wird (garantiert) zur Compiletime ausgewertet
        foo!at(); // (4) Wie (1) nur explizit, Compiler entscheidet automatisch
    
        int i1 = bar(); // Standard
        int i2 = bar!ct(); // Äquivalent zu: int i2 = 1;
        constexpr int i3 = bar(); // Compiler nimmt automatisch !ct wegen constexpr
        constexpr int i4 = bar!rt(); // Compile-Fehler
    }
    

    Dann könnte man als Benutzer immer das verwenden was man gerade braucht, hätte also vollständige Kontrolle. Außerdem müsste man gar keine Funktion mehr als constexpr deklarieren, da man dies einfach über den Aufruf steuern könnte.

    Würde etwas gegen eine solche Lösung sprechen?


  • Mod

    😕 Du musst überhaupt nichts constexpr machen. Du kannst etwas constexpr machen, wenn du möchtest und der Compiler kann dann kontrollieren, ob das auch wirklich stimmt, was du da behauptest. Und den Aufrufer interessieren solche Implementierungsdetails einen Dreck. Der freut sich entweder, dass der Code schön flott ist oder ärgert sich, wenn er es nicht ist.

    Du scheinst die Idee hinter constexpr in jedweder Hinsicht falsch verstanden zu haben.



  • SeppJ schrieb:

    😕 Du musst überhaupt nichts constexpr machen. Du kannst etwas constexpr machen, wenn du möchtest und der Compiler kann dann kontrollieren, ob das auch wirklich stimmt, was du da behauptest.

    Das ist mir schon klar. Allerdings macht es doch keinen Sinn eine Funktion nicht constexpr zu machen, wenn es prinzipiell möglich ist und auch sonst nichts dagegen spricht. Sagen wir ich habe etwas wie

    char to_lower(char c)
    {
        // Implementierung
    }
    

    und ich brauche das in meiner Anwendung sowieso nur für Strings die zur Laufzeit entstehen. Dann kann ich die Funktion einfach nehmen. Aber vielleicht brauche ich die Funktion ja doch irgendwann mal zur Compilezeit, wer weiß das schon.

    Deswegen würde ich die Funktion jetzt einfach auch schonmal vorsorglich constexpr deklarieren: Schlicht aus dem Grund weil nichts dagegen spricht.

    SeppJ schrieb:

    Und den Aufrufer interessieren solche Implementierungsdetails einen Dreck. Der freut sich entweder, dass der Code schön flott ist oder ärgert sich, wenn er es nicht ist.

    Du scheinst die Idee hinter constexpr in jedweder Hinsicht falsch verstanden zu haben.

    Das wäre ja nach wie vor gegeben.

    Wenn es dem Anwender "egal" ist was passiert, schreibt er einfach foo(); wie jetzt auch schon. Dann entscheidet der Compiler und der Aufrufer kann sich freuen oder eben nicht.

    Worum es mir geht ist aber genau der umgekehrte Fall, wenn es dem Aufrufer nicht egal ist.

    Das ist keine Einschränkung da etwas wie constexpr int i = f(); immer noch genauso funktionieren würde wie jetzt auch (habe ein paar weitere Beispiele zu dem Anfangspost hinzugefügt).


  • Mod

    Du darfst constexpr Funktionen auch zur Laufzeit mit variablen Argumenten aufrufen!



  • SeppJ schrieb:

    Du darfst constexpr Funktionen auch zur Laufzeit mit variablen Argumenten aufrufen!

    Das weiß ich. Deswegen habe ich ja auch geschrieben:

    happystudent schrieb:

    und ich brauche das in meiner Anwendung sowieso nur für Strings die zur Laufzeit entstehen.
    ...
    Deswegen würde ich die Funktion jetzt einfach auch schonmal vorsorglich constexpr deklarieren: Schlicht aus dem Grund weil nichts dagegen spricht.

    Könnte ich eine constexpr Funktion nicht auch zur Laufzeit mit variablen Argumenten aufrufen, käme sie wohl kaum für meine Anwendung in Frage (in der, wie beschrieben, ausschließlich mit Strings, welche zur Laufzeit erzeugt werden, gearbeitet wird).

    Das ist aber nicht der Punkt.

    Mir geht es u.a. darum dass man als User nicht direkt steuern kann ob eine Funktion jetzt zur Lauf- oder Compilezeit ausgewertet werden soll. Dafür braucht man dann z.B. entweder eine lokale constexpr Variable oder solche "Tricks" wie std::integral_constant (was aber auch nicht immer geht, z.B. bei doubles).

    Oder andersrum, ich habe eine constexpr Funktion die zur Laufzeit ausgewertet werden soll, aber der Compiler entscheidet sich dafür diese zur Compilezeit auszuwerten.





  • [OT]
    Gibt's eigentlich schon eine Möglichkeit unterschiedliche Implementierungen für compiletime und runtime Ausführung zu machen? Quasi constexpr Overloads. Also z.B. für Fälle wo man durch die constexpr Einschränkungen nur eine langsamere Version implementieren kann.
    [/OT]



  • happystudent schrieb:

    Dafür braucht man dann z.B. entweder eine lokale constexpr Variable

    Eben. Problem gelöst. So oft braucht man das nicht, d.h. diese zusätzliche Zeile ist durchaus akzeptabel.

    happystudent schrieb:

    Oder andersrum, ich habe eine constexpr Funktion die zur Laufzeit ausgewertet werden soll, aber der Compiler entscheidet sich dafür diese zur Compilezeit auszuwerten.

    Wofür sollte das gut sein? Also die Ausführung zur Laufzeit zu erzwingen. Davon abgesehen... es gibt ohne compilerspezifische Erweiterungen und "Tricks" sowieso nichtmal ne Möglichkeit das für normale Funktionen zu garantieren. Der Compiler darf immer inlinen und dann Dinge zur Compilezeit berechnen wenn er es hinbekommt.



  • Gast3 schrieb:

    http://stackoverflow.com/questions/14472359/why-do-we-need-to-mark-functions-as-constexpr

    Also man braucht es hauptsächlich um Compile-Zeiten und evtl. nicht benötigte Instanziierung zu reduzieren? Ist das nicht eher etwas was mir als Aufrufer relativ egal sein sollte? Dann könnte man das keyword doch auch einfach otional machen?

    Oder beziehst du dich auf die erste Antwort (die mit den meisten Upvotes)? Die überzeugt mich ehrlich gesagt nicht, weil seine Kernaussage ist ja dass constexpr quasi Teil des Interfaces ist. Also verhindern soll dass eine Änderung der Implementierung von f das Interface von f ändert.

    Genau das kann man aber ja eben nicht erreichen, weil auch wenn ich f als constexpr markiere kann ich durch eine Änderung der Implementierung dafür sorgen dass bestimmte Argumente von f nicht mehr constexpr Auswertbar sind. Womit ich wiederum bestehenden Code brechen kann ohne etwas am Interface von f geändert zu haben.

    Schlimmer sogar, als Anwender kann man solche Interface Änderungen gar nicht erkennen.

    hustbaer schrieb:

    Eben. Problem gelöst. So oft braucht man das nicht, d.h. diese zusätzliche Zeile ist durchaus akzeptabel.

    Kann man natürlich so sehen. Ich frage mich aber halt eher warum man nicht einfach dem Benutzer volle Flexibilität gibt.

    hustbaer schrieb:

    Wofür sollte das gut sein? Also die Ausführung zur Laufzeit zu erzwingen. Davon abgesehen... es gibt ohne compilerspezifische Erweiterungen und "Tricks" sowieso nichtmal ne Möglichkeit das für normale Funktionen zu garantieren. Der Compiler darf immer inlinen und dann Dinge zur Compilezeit berechnen wenn er es hinbekommt.

    Zum debuggen z.B. fände ich das sehr praktisch.

    Oder ein konkretes Beispiel hier. Hier will man ja das das Encoding zur Compilezeit passiert und das Decoding nur zur Laufzeit.

    Konsistent wäre, sowohl die encode als auch die decode Funktion als constexpr zu markieren, da ja prinzipiell beide mit Compiletime Strings arbeiten können (die encode Funktion das ja sogar soll).

    Geht aber nicht. Für die decode Funktion muss ich eine eigene Runtime-Variante anlegen, da der Compiler die Dekodierung immer zur Compilezeit durchführt. Hier würde ich ihm gerne einfach sagen können: bitte an dieser Stelle die Funktion garantiert zur Laufzeit auswerten.

    Letztendlich führt das dann halt dazu dass man solche Funktionen doppelt implementieren muss (zumindest habe ich noch keine bessere Alternative gefunden).



  • happystudent schrieb:

    Zum debuggen z.B. fände ich das sehr praktisch.

    OK. Das ist der einzige Punkt den ich halbwegs nachvollziehen kann.

    Darf aber natürlich jede Implementierung gerne irgendwelche Erweiterungen anbieten die das möglich machen. Bzw. kann man die Funktion zum Testen/Durchsteppen auch einfach so aufrufen dass der Compiler nicht "sehen" kann (darf) dass der Wert cc-konstant ist. Bei Integers z.B. einfach indem man sie in ne volatile Variable schreibe und wieder daraus zurückliest.

    happystudent schrieb:

    Oder ein konkretes Beispiel hier. Hier will man ja das das Encoding zur Compilezeit passiert und das Decoding nur zur Laufzeit.

    (...)

    Das ist super spezieller Spezialfall. Dafür willst du den Standard aufbohren? Ne...


Log in to reply