Ist es jemals schlimm, eine C ++ - Funktion constexpr zu markieren?

26

Angesichts einer sehr trivialen Funktion,

int transform(int val) {
    return (val + 7) / 8;
}

Es sollte sehr offensichtlich sein, dass es einfach ist, diese Funktion in eine constexprFunktion umzuwandeln, sodass ich sie beim Definieren von constexprVariablen verwenden kann, wie z.

constexpr int transform(int val) {
    return (val + 7) / 8;
}

Ich gehe davon aus, dass dies nur eine Verbesserung darstellt, da die Funktion immer noch in einem Nicht- constexprKontext aufgerufen werden kann und jetzt auch zur Definition von Variablen mit Konstanten zur Kompilierungszeit verwendet werden kann.

Meine Frage ist, gibt es Situationen, in denen dies eine schlechte Idee ist? constexprKann ich mit dieser Funktion jemals auf eine Situation stoßen, in der diese Funktion unter bestimmten Umständen nicht mehr verwendet werden kann oder wo sie sich schlecht verhält?

Xirema
quelle
1
Das einzige, woran ich denken konnte, waren Compiler-Bugs. Es ist möglich, dass ein rekursiver Aufruf der Funktion constexpr zu einem sehr langsamen Kompilierungsschritt oder sogar zu einem Speicherausfall des Compilers führt.
Zan Lynx

Antworten:

19

Dies ist nur dann von Bedeutung, wenn die Funktion Teil einer öffentlichen Schnittstelle ist und Sie zukünftige Versionen Ihrer API binärkompatibel halten möchten. In diesem Fall müssen Sie sorgfältig überlegen, wie Sie Ihre API weiterentwickeln möchten und wo Sie Erweiterungspunkte für zukünftige Änderungen benötigen.

Das macht einen constexprQualifier zu einer unwiderruflichen Designentscheidung. Sie können dieses Qualifikationsmerkmal nicht entfernen, ohne eine inkompatible Änderung an Ihrer API vorzunehmen. Es schränkt auch ein, wie Sie diese Funktion implementieren können, z. B. könnten Sie innerhalb dieser Funktion keine Protokollierung durchführen. Nicht jede triviale Funktion bleibt in der Ewigkeit trivial.

Das heißt, Sie sollten vorzugsweise constexprFunktionen verwenden, die von Natur aus reine Funktionen sind und die beim Kompilieren tatsächlich nützlich sind (z. B. für die Metaprogrammierung von Vorlagen). Es wäre nicht gut, Funktionen zu constexpr zu machen, nur weil die aktuelle Implementierung konstexfähig ist.

Wo keine Auswertung zur Kompilierungszeit erforderlich ist, erscheint die Verwendung von Inline-Funktionen oder Funktionen mit interner Verknüpfung angemessener constexpr. Allen diesen Varianten ist gemeinsam, dass der Funktionskörper „öffentlich“ ist und in der gleichen Zusammenstellungseinheit wie der Aufrufort zur Verfügung steht.

Wenn die betreffende Funktion nicht Teil einer stabilen öffentlichen API ist, ist dies weniger problematisch, da Sie das Design beliebig ändern können. Da Sie jetzt alle Anrufseiten steuern, ist es nicht erforderlich, eine Funktion constexpr als "nur für den Fall" zu markieren. Sie wissen, ob Sie diese Funktion in einem Constexpr-Kontext verwenden. Das Hinzufügen unnötig restriktiver Qualifikationsmerkmale kann dann als Verschleierung angesehen werden.

amon
quelle
12

Durch Markieren einer Funktion als constexprwird sie auch zu einer Inline-Funktion. § [dcl.constexpr] / 1:

Eine mit dem constexpr-Bezeichner deklarierte Funktion oder ein statisches Datenelement ist implizit eine Inline-Funktion oder -Variable (7.1.6).

inlineDies bedeutet wiederum, dass Sie die Definition dieser Funktion in jede Übersetzungseinheit aufnehmen müssen, in der sie verwendet werden kann. Das bedeutet im Grunde, dass constexprFunktionen entweder sein müssen:

  1. beschränkt auf die Verwendung in einer Übersetzungseinheit oder
  2. in einem Header definiert.

Die meisten typischen Funktionen, die Sie in einem Header deklarieren und in einer Quelldatei definieren möchten (und alle anderen Funktionen, die sie verwenden, enthalten nur den Header und Verknüpfungen mit der Objektdatei dieser Quelle) constexpr, funktionieren einfach nicht.

Theoretisch könnte man einfach alles in Header verschieben und nur eine Quelldatei haben, die nur alle Header enthält, aber dies würde die Kompilierzeiten drastisch beeinträchtigen und für die meisten ernsthaften Projekte würde das Kompilieren immens viel Speicher erfordern.

Eine constexprFunktion ist auch in gewisser Weise eingeschränkt, so dass sie für einige Funktionen möglicherweise überhaupt keine Option darstellt. Die Einschränkungen umfassen:

  1. virtuelle Funktionen können nicht sein constexpr.
  2. Der Rückgabetyp muss ein "Literaler Typ" sein (z. B. keine Objekte mit nicht trivalisierenden ctors oder dtors).
  3. Alle Parameter müssen Literal-Typen sein.
  4. Der Funktionskörper darf keinen tryBlock enthalten .
  5. Es darf keine Variablendefinition eines nicht-literalen Typs oder irgendetwas mit statischer oder Thread-Speicherdauer enthalten.

Ich habe ein paar ziemlich undurchsichtige Dinge übersprungen (z. B. kann es auch kein gotooder ein asmStatement enthalten), aber Sie haben die Idee - für einige Dinge funktioniert es einfach nicht.

Fazit: Ja, es gibt einige Situationen, in denen dies eine schlechte Idee wäre.

Jerry Sarg
quelle
"es darf nicht virtuell sein (bis C ++ 20)" Ich frage mich, wie eine virtuelle Funktion constexpr sein kann? Was machen Compiler?
Chaosink