Es scheint mir, dass eine "Funktion, die immer 5 zurückgibt" die Bedeutung von "Aufrufen einer Funktion" bricht oder verwässert. Es muss einen Grund oder eine Notwendigkeit für diese Funktion geben, sonst wäre es nicht in C ++ 11. Warum ist es dort?
// preprocessor.
#define MEANING_OF_LIFE 42
// constants:
const int MeaningOfLife = 42;
// constexpr-function:
constexpr int MeaningOfLife () { return 42; }
Es scheint mir, dass wenn ich eine Funktion schreibe, die einen Literalwert zurückgibt, und ich zu einer Codeüberprüfung komme, mir jemand sagt, ich sollte dann einen konstanten Wert deklarieren, anstatt return 5 zu schreiben.
constexpr
? Wenn ja, kann ich eine Verwendung sehen.const
. In der Tat beauftragt Absicht ist nützlich ! Array-Dimensionen sind das kanonische Beispiel.Antworten:
Angenommen, es macht etwas etwas komplizierter.
Jetzt haben Sie etwas, das bis auf eine Konstante ausgewertet werden kann, während die Lesbarkeit erhalten bleibt und eine etwas komplexere Verarbeitung möglich ist, als nur eine Konstante auf eine Zahl zu setzen.
Grundsätzlich bietet es eine gute Hilfe für die Wartbarkeit, da klarer wird, was Sie tun. Nehmen Sie
max( a, b )
zum Beispiel:Dort ist es eine ziemlich einfache Wahl, aber es bedeutet, dass wenn Sie
max
mit konstanten Werten aufrufen , diese explizit zur Kompilierungszeit und nicht zur Laufzeit berechnet wird.Ein weiteres gutes Beispiel wäre eine
DegreesToRadians
Funktion. Jeder findet Abschlüsse leichter zu lesen als Bogenmaß. Während Sie vielleicht wissen, dass 180 Grad im Bogenmaß sind, ist es viel klarer wie folgt geschrieben:Viele gute Infos hier:
http://en.cppreference.com/w/cpp/language/constexpr
quelle
Einführung
constexpr
wurde nicht eingeführt, um der Implementierung mitzuteilen, dass etwas in einem Kontext bewertet werden kann, der einen konstanten Ausdruck erfordert ; Konforme Implementierungen konnten dies vor C ++ 11 beweisen.Was eine Implementierung nicht beweisen kann, ist die Absicht eines bestimmten Codes:
Was wäre die Welt ohne
constexpr
?Angenommen, Sie entwickeln eine Bibliothek und stellen fest, dass Sie die Summe jeder Ganzzahl im Intervall berechnen möchten
(0,N]
.Der Mangel an Absicht
Ein Compiler kann leicht beweisen, dass die obige Funktion in einem Konstantenausdruck aufrufbar ist, wenn das übergebene Argument während der Übersetzung bekannt ist. Aber Sie haben dies nicht als Absicht deklariert - es war einfach so.
Jetzt kommt jemand anderes, liest Ihre Funktion und führt die gleiche Analyse durch wie der Compiler. " Oh, diese Funktion kann in einem konstanten Ausdruck verwendet werden!" und schreibt den folgenden Code.
Die Optimierung
Sie als "großartiger" Bibliotheksentwickler entscheiden, dass
f
das Ergebnis beim Aufrufen zwischengespeichert werden soll. Wer möchte immer wieder die gleichen Werte berechnen?Das Ergebnis
Durch die Einführung Ihrer albernen Optimierung haben Sie einfach jede Verwendung Ihrer Funktion unterbrochen, die sich zufällig in einem Kontext befand, in dem ein konstanter Ausdruck erforderlich war.
Sie haben nie versprochen, dass die Funktion in einem konstanten Ausdruck verwendet werden kann , und ohne sie
constexpr
gäbe es keine Möglichkeit, ein solches Versprechen abzugeben.Also, warum brauchen wir
constexpr
?Die Hauptverwendung von constexpr besteht darin, die Absicht zu erklären .
Wenn eine Entität nicht als gekennzeichnet ist, sollte
constexpr
sie niemals in einem konstanten Ausdruck verwendet werden . und selbst wenn dies der Fall ist, verlassen wir uns darauf, dass der Compiler einen solchen Kontext diagnostiziert (weil er unsere Absicht missachtet).quelle
constexpr
Ausdrücken ermöglichen. Mit anderen Worten, so ziemlich alles kann mit Anmerkungen versehen werdenconstexpr
(vielleicht verschwindet es eines Tages einfach?), Und wenn man kein Kriterium dafür hat, wann man es verwendetconstexpr
oder nicht, wird so ziemlich der gesamte Code als solcher geschrieben .I/O
,syscall
unddynamic memory allocation
definitly cann't als markiertconstexpr
Außerdem nicht alles sollte seinconstexpr
.constexpr
ist eine Garantie für ein Verhalten. Genau wieconst
.int f (int n) { return n > 0 ? n + f (n-1) : n;} T arr[f(10)];
Ich kann sie nirgendwo kompilieren lassen?Nehmen Sie
std::numeric_limits<T>::max()
: aus welchem Grund auch immer, dies ist eine Methode.constexpr
wäre hier von Vorteil.Ein weiteres Beispiel: Sie möchten ein C-Array (oder a
std::array
) deklarieren , das so groß ist wie ein anderes Array. Der Weg, dies im Moment zu tun, ist wie folgt:Aber wäre es nicht besser schreiben zu können:
Dank
constexpr
können Sie:quelle
constexpr
zwingt den Complier, die Funktion einen Wert zur Kompilierungszeit zurückgeben zu lassen (falls möglich).constexpr
kann es weder in einer Array-Größendeklaration noch als Vorlagenargument verwendet werden, unabhängig davon, ob das Ergebnis des Funktionsaufrufs eine Konstante zur Kompilierungszeit ist oder nicht. Diese beiden sind im Grunde die einzigen Anwendungsfälle,constexpr
aber zumindest der Anwendungsfall des Vorlagenarguments ist irgendwie wichtig.-pedantic
Option und sie wird als Fehler gekennzeichnet.constexpr
Funktionen sind wirklich nett und eine großartige Ergänzung zu C ++. Sie haben jedoch Recht, dass die meisten Probleme, die es löst, unelegant mit Makros umgangen werden können.Eine der Verwendungen von
constexpr
hat jedoch keine C ++ 03-äquivalenten typisierten Konstanten.quelle
four
nicht aufgelöst wird. Ich musste wirklich graben, um herauszufinden, wer die Adresse meinerstatic const
Variablen nahm.four
nochfive
im Geltungsbereich.enum class
Typ, der einige der Enum-Probleme behebt.Nach dem, was ich gelesen habe, ergibt sich die Notwendigkeit von constexpr aus einem Problem bei der Metaprogrammierung. Für Merkmalsklassen können Konstanten als Funktionen dargestellt werden. Denken Sie daran: numeric_limits :: max (). Mit constexpr können diese Arten von Funktionen in der Metaprogrammierung oder als Array-Grenzen usw. usw. verwendet werden.
Ein weiteres Beispiel wäre, dass Sie für Klassenschnittstellen möglicherweise möchten, dass abgeleitete Typen für einige Operationen ihre eigenen Konstanten definieren.
Bearbeiten:
Nach Stossen auf SO herum, sieht es aus wie andere kommen mit haben einige Beispiele von dem, was mit constexprs möglich sein könnte.
quelle
constexpr
ist insbesondere in einem Compiler mit einem leistungsstarken Bewertungssystem für Ausdrücke zur Kompilierungszeit nützlich. C ++ hat wirklich keine Peers in dieser Domäne. (das ist ein starkes Lob für C ++ 11, IMHO)Aus Stroustrups Rede bei "Going Native 2012":
quelle
Eine andere Verwendung (noch nicht erwähnt) sind
constexpr
Konstruktoren. Auf diese Weise können Konstanten für die Kompilierungszeit erstellt werden, die zur Laufzeit nicht initialisiert werden müssen.Wenn Sie dies mit benutzerdefinierten Literalen kombinieren, haben Sie volle Unterstützung für benutzerdefinierte Literalklassen.
quelle
Früher gab es ein Muster mit Metaprogrammierung:
Ich glaube, es
constexpr
wurde eingeführt, damit Sie solche Konstrukte schreiben können, ohne Vorlagen und seltsame Konstrukte mit Spezialisierung, SFINAE und anderen Dingen zu benötigen - aber genau so, als würden Sie eine Laufzeitfunktion schreiben, aber mit der Garantie, dass das Ergebnis beim Kompilieren ermittelt wird -Zeit.Beachten Sie jedoch Folgendes:
Kompilieren Sie dies mit
g++ -O3
und Sie werden sehen, dass diesfact(10)
tatsächlich zur Kompilierungszeit ausgewertet wird!Ein VLA-fähiger Compiler (also ein C-Compiler im C99-Modus oder ein C ++ - Compiler mit C99-Erweiterungen) kann Ihnen sogar Folgendes ermöglichen:
Aber dass es sich im Moment nicht um Standard-C ++ handelt,
constexpr
scheint eine Möglichkeit zu sein, dies zu bekämpfen (im obigen Fall auch ohne VLA). Und es gibt immer noch das Problem, dass "formale" konstante Ausdrücke als Vorlagenargumente erforderlich sind.quelle
std::array<int, fact(2)>
und Sie werden sehen, dass fact () beim Kompilieren nicht ausgewertet wird. Es ist nur der GCC-Optimierer, der gute Arbeit leistet.Ich habe gerade angefangen, ein Projekt auf c ++ 11 umzustellen, und bin auf eine vollkommen gute Situation für constexpr gestoßen, die alternative Methoden zur Ausführung derselben Operation bereinigt. Der entscheidende Punkt hierbei ist, dass Sie die Funktion nur dann in die Arraygrößendeklaration einfügen können, wenn sie als constexpr deklariert ist. Es gibt eine Reihe von Situationen, in denen ich sehe, dass dies in Bezug auf den Codebereich, an dem ich beteiligt bin, sehr nützlich ist.
quelle
static inline constexpr const auto
wahrscheinlich ist besser.Alle anderen Antworten sind großartig. Ich möchte nur ein cooles Beispiel für eine Sache geben, die Sie mit constexpr machen können, die erstaunlich ist. See-Phit ( https://github.com/rep-movsd/see-phit/blob/master/seephit.h ) ist eine HTML -Parser- und Vorlagen-Engine zur Kompilierungszeit. Dies bedeutet, dass Sie HTML einfügen und einen Baum herausholen können, der bearbeitet werden kann. Wenn Sie das Parsen zur Kompilierungszeit durchführen, erhalten Sie zusätzliche Leistung.
Aus dem Beispiel der Github-Seite:
quelle
Ihr grundlegendes Beispiel dient demselben Argument wie das der Konstanten selbst. Warum verwenden
Über
Weil es viel wartbarer ist. Die Verwendung von constexpr ist viel, viel schneller zu schreiben und zu lesen als die vorhandenen Metaprogrammiertechniken.
quelle
Es kann einige neue Optimierungen ermöglichen.
const
Traditionell ist ein Hinweis für das Typsystem, und nicht für die Optimierung verwendet werden kann (zB einconst
Member - Funktion kannconst_cast
und das Objekt ohnehin ändern, rechtlich, soconst
kann nicht für die Optimierung vertraut werden).constexpr
bedeutet, dass der Ausdruck wirklich konstant ist, vorausgesetzt, die Eingaben für die Funktion sind const. Erwägen:Wenn dies in einem anderen Modul verfügbar gemacht wird, kann der Compiler nicht darauf vertrauen, dass
GetNumber()
bei jedem Aufruf keine anderen Werte zurückgegeben werden - auch nicht nacheinander ohne dazwischen liegende Nicht-Konstanten-Aufrufe -, daconst
dies in der Implementierung möglicherweise verworfen wurde. (Natürlich sollte jeder Programmierer, der dies getan hat, erschossen werden, aber die Sprache erlaubt es, daher muss der Compiler die Regeln einhalten.)Hinzufügen
constexpr
:Der Compiler kann jetzt eine Optimierung anwenden, bei der der Rückgabewert von
GetNumber()
zwischengespeichert wird, und zusätzliche Aufrufe von eliminierenGetNumber()
, da diesconstexpr
eine stärkere Garantie dafür ist, dass sich der Rückgabewert nicht ändert.quelle
const
kann in der Optimierung verwendet werden ... Es ist nicht definiertes Verhalten Wert ändern const definierte auch nach einemconst_cast
IIRC. Ich würde erwarten, dass es fürconst
Mitgliedsfunktionen konsistent ist, aber ich müsste das mit dem Standard überprüfen. Dies würde bedeuten, dass der Compiler dort sicher Optimierungen vornehmen kann.int x
vs.const int x
) deklariert wurde , es sicher geändert werden kann, indemconst_cast
const auf einem Zeiger / Verweis darauf entfernt wird. Andernfallsconst_cast
würde immer undefiniertes Verhalten aufgerufen und wäre nutzlos :) In diesem Fall hat der Compiler keine Informationen über die Konstanz des ursprünglichen Objekts, sodass er dies nicht erkennen kann.int GetNumber() const = 0;
) sollte dieGetNumber()
Methode als virtuell deklarieren . Das zweite (constexpr int GetNumber() const = 0;
) ist nicht gültig, da der reine Spezifizierer (= 0
) impliziert, dass die Methode virtuell ist, aber constexprs dürfen nicht virtuell sein (ref: en.cppreference.com/w/cpp/language/constexpr )Wann zu verwenden
constexpr
:quelle
constexpr
Präprozessor-Makros oder bevorzugt werden solltenconst
.Es ist nützlich für so etwas
Binden Sie dies mit einer Merkmalsklasse oder ähnlichem zusammen und es wird ziemlich nützlich.
quelle