Gibt es einen Unterschied zwischen den folgenden Definitionen?
const double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
Wenn nicht, welcher Stil wird in C ++ 11 bevorzugt?
Gibt es einen Unterschied zwischen den folgenden Definitionen?
const double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
Wenn nicht, welcher Stil wird in C ++ 11 bevorzugt?
Antworten:
Ich glaube, es gibt einen Unterschied. Benennen wir sie um, damit wir leichter darüber sprechen können:
const double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;
Beide PI1
und PI2
sind konstant, dh Sie können sie nicht ändern. Es ist jedoch nur PI2
eine Kompilierungszeitkonstante. Es wird zur Kompilierungszeit initialisiert. PI1
kann zur Kompilierungs- oder Laufzeit initialisiert werden. Darüber hinaus kann nur PI2
in einem Kontext verwendet werden, der eine Kompilierungszeitkonstante erfordert. Zum Beispiel:
constexpr double PI3 = PI1; // error
aber:
constexpr double PI3 = PI2; // ok
und:
static_assert(PI1 == 3.141592653589793, ""); // error
aber:
static_assert(PI2 == 3.141592653589793, ""); // ok
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.
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 Konvertierbarkeit PI1
zur Kompilierungszeit in einen integralen Typ scheint mir also ein kleiner Hit & Miss zu sein.
enum
Initialisierer die einzigen zwei bemerkenswerten Unterschiede zwischen const
und constexpr
(und keiner von beiden funktioniert double
trotzdem).
1 / PI1
und 1 / 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.
struct S {
constexpr S(int);
};
const S s0(0);
constexpr S s1(1);
s0
ist eine Konstante, verspricht jedoch nicht, zur Kompilierungszeit initialisiert zu werden. s1
ist markiert constexpr
, es ist also eine Konstante, und da S
der 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
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 als const
und nicht wie folgt deklariert werden constexpr
: dh würde constexpr int foo(S)
zur Kompilierungszeit ausgeführt, wenn ich aufrufe foo(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.
int sz;
constexpr auto arraySize1 = sz; // error! sz's value unknown at compilation
std::array<int, sz> data1; // error! same problem
constexpr auto 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;
const auto 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.
Einer constexpr-Symbolkonstante muss ein Wert zugewiesen werden, der zur Kompilierungszeit bekannt ist. Zum Beispiel:
constexpr int max = 100;
void use(int n)
{
constexpr int c1 = max+7; // OK: c1 is 107
constexpr int 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:
constexpr int max = 100;
void use(int n)
{
constexpr int c1 = max+7; // OK: c1 is 107
const int 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:
Referenz: "Programmierung: Prinzipien und Praxis mit C ++" von Stroustrup