Beide sind Kompilierungszeitkonstanten. Sie können jedoch einen const_cast des ersten erstellen und darauf schreiben. Es wird jedoch von jedem Compiler optimiert, da dies keinen Einfluss auf "Lesevorgänge" hat, wie sie zur Kompilierungszeit auftreten.
Bonita Montero
Antworten:
347
Ich glaube, es gibt einen Unterschied. Benennen wir sie um, damit wir leichter darüber sprechen können:
Beide PI1und PI2sind konstant, dh Sie können sie nicht ändern. Es ist jedoch nurPI2 eine Kompilierungszeitkonstante. Es wird zur Kompilierungszeit initialisiert. PI1kann zur Kompilierungs- oder Laufzeit initialisiert werden. Darüber hinaus kann nurPI2 in einem Kontext verwendet werden, der eine Kompilierungszeitkonstante erfordert. Zum Beispiel:
Was solltest du verwenden? Verwenden Sie, was Ihren Anforderungen entspricht. Möchten Sie sicherstellen, dass Sie eine Kompilierungszeitkonstante haben, die in Kontexten verwendet werden kann, in denen eine Kompilierungszeitkonstante erforderlich ist? Möchten Sie es mit einer zur Laufzeit durchgeführten Berechnung initialisieren können? Usw.
Bist du sicher? Weil const int N = 10; char a[N];funktioniert und Array-Grenzen Konstanten zur Kompilierungszeit sein müssen.
Fredoverflow
10
Ich bin mir sicher, was die Beispiele angeht, die ich geschrieben habe (jedes vor dem Posten getestet). Mein Compiler lässt mich jedoch PI1zur Verwendung in einem Array in eine Integralkonstante zur Kompilierungszeit konvertieren , jedoch nicht zur Verwendung als integraler Vorlagenparameter ohne Typ. Die Konvertierbarkeit PI1zur Kompilierungszeit in einen integralen Typ scheint mir also ein kleiner Hit & Miss zu sein.
Howard Hinnant
34
@FredOverflow: Nicht-konstante Array-Indizes haben ungefähr ein Jahrzehnt lang "funktioniert" (es gibt zum Beispiel eine g ++ - Erweiterung dafür), aber das bedeutet nicht, dass es streng legal C ++ ist (obwohl ein neuerer C- oder C ++ - Standard es legal gemacht hat , ich vergessen welche). Was die Unterschiede bei den Kompilierungszeitkonstanten betrifft, so sind Vorlagenparameter und die Verwendung als enumInitialisierer die einzigen zwei bemerkenswerten Unterschiede zwischen constund constexpr(und keiner von beiden funktioniert doubletrotzdem).
Damon
17
Absatz 4 von 5.19 Konstante Ausdrücke [expr.const] ist auch eine (nicht normative) Anmerkung, die bekanntermaßen umreißt, dass eine Implementierung Gleitkomma-Arithmetik zur Kompilierungszeit anders ausführen darf (z. B. in Bezug auf die Genauigkeit) als zur Laufzeit. Also 1 / PI1und 1 / PI2kann zu unterschiedlichen Ergebnissen führen. Ich denke jedoch nicht, dass diese Technik so wichtig ist wie der Rat in dieser Antwort.
Luc Danton
4
Aber es constexpr double PI3 = PI1;funktioniert richtig für mich. (MSVS2013 CTP). Was mache ich falsch?
NuPagadi
77
Kein Unterschied, aber es ist wichtig, wenn Sie einen Typ haben, der einen Konstruktor hat.
struct S {constexpr S(int);};const S s0(0);constexpr S s1(1);
s0ist eine Konstante, verspricht jedoch nicht, zur Kompilierungszeit initialisiert zu werden. s1ist markiert constexpr, es ist also eine Konstante, und da Sder Konstruktor ebenfalls markiert ist constexpr, wird er zur Kompilierungszeit initialisiert.
Meistens ist dies wichtig, wenn die Initialisierung zur Laufzeit zeitaufwändig wäre und Sie diese Arbeit auf den Compiler übertragen möchten, wo sie ebenfalls zeitaufwändig ist, aber die Ausführungszeit des kompilierten Programms nicht verlangsamt
Ich stimme zu: Die Schlussfolgerung, zu der ich gekommen bin, war, dass constexprzu einer Diagnose führen würde, falls die Berechnung des Objekts zur Kompilierungszeit unmöglich sein sollte. Weniger klar ist, ob eine Funktion, die einen konstanten Parameter erwartet , zur Kompilierungszeit ausgeführt werden kann, sollte der Parameter als constund nicht wie folgt deklariert werden constexpr: dh würde constexpr int foo(S)zur Kompilierungszeit ausgeführt, wenn ich aufrufe foo(s0)?
Matthieu M.
4
@MatthieuM: Ich bezweifle, ob foo(s0)es zur Kompilierungszeit ausgeführt wird, aber Sie wissen nie: Ein Compiler darf solche Optimierungen vornehmen . Weder gcc 4.7.2 noch clang 3.2 erlauben es mir,constexpr a = foo(s0);
rici
50
constexpr gibt einen Wert an, der konstant ist und während der Kompilierung bekannt ist. const gibt einen Wert an, der nur konstant ist; Es ist nicht obligatorisch, dies während der Kompilierung zu wissen.
int sz;constexprauto arraySize1 = sz;// error! sz's value unknown at compilation
std::array<int, sz> data1;// error! same problemconstexprauto arraySize2 =10;// fine, 10 is a compile-time constant
std::array<int, arraySize2> data2;// fine, arraySize2 is constexpr
Beachten Sie, dass const nicht die gleiche Garantie wie constexpr bietet, da const-Objekte nicht mit Werten initialisiert werden müssen, die während der Kompilierung bekannt sind.
int sz;constauto arraySize = sz;// fine, arraySize is const copy of sz
std::array<int, arraySize> data;// error! arraySize's value unknown at compilation
Alle constexpr-Objekte sind const, aber nicht alle const-Objekte sind constexpr.
Wenn Sie möchten, dass Compiler sicherstellen, dass eine Variable einen Wert hat, der in Kontexten verwendet werden kann, in denen Konstanten zur Kompilierungszeit erforderlich sind, müssen Sie nach constexpr und nicht nach const greifen.
Ihre Erklärung hat mir sehr gut gefallen. Können Sie bitte mehr dazu sagen? Wo sind die Fälle, in denen wir möglicherweise Kompilierungszeitkonstanten in realen Szenarien verwenden müssen?
Einer constexpr-Symbolkonstante muss ein Wert zugewiesen werden, der zur Kompilierungszeit bekannt ist. Zum Beispiel:
constexprint max =100;void use(int n){constexprint c1 = max+7;// OK: c1 is 107constexprint c2 = n+7;// Error: we don’t know the value of c2// ...}
Um Fälle zu behandeln , in denen der Wert einer „Variable“ , die mit einem Wert initialisiert wird, der nicht bei der Kompilierung bekannt ist , aber nie ändert nach der Initialisierung, C ++ bietet eine zweite Form konstant (a const ). Beispielsweise:
constexprint max =100;void use(int n){constexprint c1 = max+7;// OK: c1 is 107constint c2 = n+7;// OK, but don’t try to change the value of c2// ...
c2 =7;// error: c2 is a const}
Solche " const- Variablen" sind aus zwei Gründen sehr häufig:
C ++ 98 hatte kein constexpr, daher verwendeten die Leute const .
Listenelemente „Variablen“, die keine konstanten Ausdrücke sind (ihr Wert ist zum Zeitpunkt der Kompilierung nicht bekannt), aber nach der Initialisierung keine Werte ändern, sind an sich sehr nützlich.
Referenz: "Programmierung: Prinzipien und Praxis mit C ++" von Stroustrup
Antworten:
Ich glaube, es gibt einen Unterschied. Benennen wir sie um, damit wir leichter darüber sprechen können:
Beide
PI1
undPI2
sind konstant, dh Sie können sie nicht ändern. Es ist jedoch nurPI2
eine Kompilierungszeitkonstante. Es wird zur Kompilierungszeit initialisiert.PI1
kann zur Kompilierungs- oder Laufzeit initialisiert werden. Darüber hinaus kann nurPI2
in einem Kontext verwendet werden, der eine Kompilierungszeitkonstante erfordert. Zum Beispiel:aber:
und:
aber:
Was solltest du verwenden? Verwenden Sie, was Ihren Anforderungen entspricht. Möchten Sie sicherstellen, dass Sie eine Kompilierungszeitkonstante haben, die in Kontexten verwendet werden kann, in denen eine Kompilierungszeitkonstante erforderlich ist? Möchten Sie es mit einer zur Laufzeit durchgeführten Berechnung initialisieren können? Usw.
quelle
const int N = 10; char a[N];
funktioniert und Array-Grenzen Konstanten zur Kompilierungszeit sein müssen.PI1
zur Verwendung in einem Array in eine Integralkonstante zur Kompilierungszeit konvertieren , jedoch nicht zur Verwendung als integraler Vorlagenparameter ohne Typ. Die KonvertierbarkeitPI1
zur Kompilierungszeit in einen integralen Typ scheint mir also ein kleiner Hit & Miss zu sein.enum
Initialisierer die einzigen zwei bemerkenswerten Unterschiede zwischenconst
undconstexpr
(und keiner von beiden funktioniertdouble
trotzdem).1 / PI1
und1 / PI2
kann zu unterschiedlichen Ergebnissen führen. Ich denke jedoch nicht, dass diese Technik so wichtig ist wie der Rat in dieser Antwort.constexpr double PI3 = PI1;
funktioniert richtig für mich. (MSVS2013 CTP). Was mache ich falsch?Kein Unterschied, aber es ist wichtig, wenn Sie einen Typ haben, der einen Konstruktor hat.
s0
ist eine Konstante, verspricht jedoch nicht, zur Kompilierungszeit initialisiert zu werden.s1
ist markiertconstexpr
, es ist also eine Konstante, und daS
der Konstruktor ebenfalls markiert istconstexpr
, wird er zur Kompilierungszeit initialisiert.Meistens ist dies wichtig, wenn die Initialisierung zur Laufzeit zeitaufwändig wäre und Sie diese Arbeit auf den Compiler übertragen möchten, wo sie ebenfalls zeitaufwändig ist, aber die Ausführungszeit des kompilierten Programms nicht verlangsamt
quelle
constexpr
zu einer Diagnose führen würde, falls die Berechnung des Objekts zur Kompilierungszeit unmöglich sein sollte. Weniger klar ist, ob eine Funktion, die einen konstanten Parameter erwartet , zur Kompilierungszeit ausgeführt werden kann, sollte der Parameter alsconst
und nicht wie folgt deklariert werdenconstexpr
: dh würdeconstexpr int foo(S)
zur Kompilierungszeit ausgeführt, wenn ich aufrufefoo(s0)
?foo(s0)
es zur Kompilierungszeit ausgeführt wird, aber Sie wissen nie: Ein Compiler darf solche Optimierungen vornehmen . Weder gcc 4.7.2 noch clang 3.2 erlauben es mir,constexpr a = foo(s0);
constexpr gibt einen Wert an, der konstant ist und während der Kompilierung bekannt ist.
const gibt einen Wert an, der nur konstant ist; Es ist nicht obligatorisch, dies während der Kompilierung zu wissen.
Beachten Sie, dass const nicht die gleiche Garantie wie constexpr bietet, da const-Objekte nicht mit Werten initialisiert werden müssen, die während der Kompilierung bekannt sind.
Alle constexpr-Objekte sind const, aber nicht alle const-Objekte sind constexpr.
Wenn Sie möchten, dass Compiler sicherstellen, dass eine Variable einen Wert hat, der in Kontexten verwendet werden kann, in denen Konstanten zur Kompilierungszeit erforderlich sind, müssen Sie nach constexpr und nicht nach const greifen.
quelle
Einer constexpr-Symbolkonstante muss ein Wert zugewiesen werden, der zur Kompilierungszeit bekannt ist. Zum Beispiel:
Um Fälle zu behandeln , in denen der Wert einer „Variable“ , die mit einem Wert initialisiert wird, der nicht bei der Kompilierung bekannt ist , aber nie ändert nach der Initialisierung, C ++ bietet eine zweite Form konstant (a const ). Beispielsweise:
Solche " const- Variablen" sind aus zwei Gründen sehr häufig:
Referenz: "Programmierung: Prinzipien und Praxis mit C ++" von Stroustrup
quelle