it-swarm.com.de

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

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 constexpr -Funktion umzuwandeln, sodass ich sie beim Definieren von constexpr -Variablen verwenden kann, wie folgt:

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

Ich gehe davon aus, dass dies eine strikte Verbesserung darstellt, da die Funktion weiterhin in einem Nicht-constexpr -Kontext aufgerufen werden kann und nun auch zur Definition von Konstantenvariablen zur Kompilierungszeit verwendet werden kann.

Meine Frage ist: Gibt es Situationen, in denen dies eine schlechte Idee ist? Wenn ich diese Funktion constexpr mache, kann ich jemals auf eine Situation stoßen, in der diese Funktion in a nicht mehr verwendet werden kann besondere Umstände, oder wo wird es sich schlecht benehmen?

26
Xirema

Dies ist nur wichtig, wenn die Funktion Teil einer öffentlichen Schnittstelle ist und Sie zukünftige Versionen Ihrer API binär kompatibel 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 ein constexpr Qualifikationsmerkmal zu einer unwiderruflichen Entwurfsentscheidung. Sie können dieses Qualifikationsmerkmal nicht entfernen, ohne Ihre API inkompatibel zu ändern. Es schränkt auch ein, wie Sie diese Funktion implementieren können, z. Innerhalb dieser Funktion können Sie keine Protokollierung durchführen. Nicht jede triviale Funktion wird in der Ewigkeit trivial bleiben.

Das bedeutet, dass Sie vorzugsweise constexpr für Funktionen verwenden sollten, die inhärent reine Funktionen sind und die zur Kompilierungszeit tatsächlich nützlich wären (z. B. für die Metaprogrammierung von Vorlagen). Es wäre nicht gut, Funktionen constexpr zu machen, nur weil die aktuelle Implementierung constexpr-fähig ist.

Wenn keine Auswertung zur Kompilierungszeit erforderlich ist, erscheint die Verwendung von Inline-Funktionen oder Funktionen mit interner Verknüpfung angemessener als constexpr. Allen diesen Varianten ist gemeinsam, dass der Funktionskörper „öffentlich“ ist und in derselben Kompilierungseinheit wie der Aufrufort verfügbar ist.

Wenn die betreffende Funktion nicht Teil einer stabilen öffentlichen API ist, ist dies weniger problematisch, da Sie das Design nach Belieben beliebig ändern können. Da Sie jetzt alle Anrufstellen steuern, ist es nicht erforderlich, eine Funktion constexpr "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 betrachtet werden.

19
amon

Wenn Sie eine Funktion als constexpr markieren, wird 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).

inline 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 constexpr Funktionen 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 alles andere, das sie verwendet, enthält nur den Header und dann Verknüpfungen mit der Objektdatei dieser Quelle) constexpr funktioniert einfach nicht.

Theoretisch könnte man einfach alles in Header verschieben und nur eine Quelldatei haben, die nur alle Header enthält. Dies würde jedoch die Kompilierungszeiten drastisch beeinträchtigen und für die meisten ernsthaften Projekte immense Mengen an Speicher zum Kompilieren erfordern.

Eine constexpr -Funktion ist in gewisser Weise ebenfalls eingeschränkt, sodass sie für einige Funktionen möglicherweise überhaupt keine Option ist. Die Einschränkungen umfassen:

  1. virtuelle Funktionen kann nicht sein constexpr.
  2. sein Rückgabetyp muss ein "Literaltyp" sein (z. B. keine Objekte mit nicht-trivalenten ctors oder dtors).
  3. alle Parameter müssen Literaltypen sein.
  4. der Funktionskörper darf keinen try -Block enthalten.
  5. es darf keine Variablendefinition eines nicht-literalen Typs oder irgendetwas mit statischer oder Thread-Speicherdauer enthalten.

Ich habe ein paar ziemlich dunkle Dinge übersprungen (z. B. kann es auch keine goto oder asm Anweisung enthalten), aber Sie haben die Idee - für einige Dinge, es wird einfach nicht funktionieren.

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

12
Jerry Coffin