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 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- constexpr
Kontext 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? constexpr
Kann 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?
Antworten:
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
constexpr
Qualifier 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
constexpr
Funktionen 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.
quelle
Durch Markieren einer Funktion als
constexpr
wird sie auch zu einer Inline-Funktion. § [dcl.constexpr] / 1:inline
Dies bedeutet wiederum, dass Sie die Definition dieser Funktion in jede Übersetzungseinheit aufnehmen müssen, in der sie verwendet werden kann. Das bedeutet im Grunde, dassconstexpr
Funktionen entweder sein müssen: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
constexpr
Funktion ist auch in gewisser Weise eingeschränkt, so dass sie für einige Funktionen möglicherweise überhaupt keine Option darstellt. Die Einschränkungen umfassen:constexpr
.try
Block enthalten .Ich habe ein paar ziemlich undurchsichtige Dinge übersprungen (z. B. kann es auch kein
goto
oder einasm
Statement 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.
quelle