2012-11-12 7 views
177

Gibt es einen Unterschied zwischen den folgenden Definitionen?const vs conexpr auf Variablen

const  double PI = 3.141592653589793; 
constexpr double PI = 3.141592653589793; 

Wenn nicht, welcher Stil wird in C++ 11 bevorzugt?

+2

Superset: http://stackoverflow.com/questions/14116003/difference-between-constexpr-and-const –

Antwort

197

Ich glaube es gibt einen Unterschied. Lassen Sie uns sie umbenennen, so dass wir über sie leicht mehr sprechen kann:

const  double PI1 = 3.141592653589793; 
constexpr double PI2 = 3.141592653589793; 

Sowohl PI1 und PI2 konstant sind, dh Sie können sie nicht ändern können. nurPI2 ist eine Kompilierzeitkonstante. Es wird soll initialisiert werden zu kompilierter Zeit. PI1 kann zur Kompilierzeit oder zur Laufzeit initialisiert werden. Außerdem kann nurPI2 in einem Kontext verwendet werden, der eine Kompilierzeitkonstante 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 

In Bezug auf die sollten Sie verwenden? Verwenden Sie, was immer Ihren Bedürfnissen entspricht. Möchten Sie sicherstellen, dass Sie über eine Kompilierzeitkonstante verfügen, die in Kontexten verwendet werden kann, in denen eine Kompilierzeitkonstante erforderlich ist? Möchten Sie in der Lage sein, es mit einer zur Laufzeit ausgeführten Berechnung zu initialisieren? Usw.

+39

Sind Sie sicher? Weil const int N = 10; char a [N]; 'funktioniert, und Array-Grenzen müssen Kompilierzeitkonstanten sein. – fredoverflow

+9

Ich bin sicher, soweit die Beispiele, die ich geschrieben habe, gehen (getestet jedes von ihnen vor dem Posten). Jedoch erlaubt mir mein Compiler, 'PI1' in eine Kompilierzeit-Integralkonstante zu konvertieren, die in einem Array verwendet wird, aber nicht als ein nicht-typischer integraler Template-Parameter. Die Kompilierbarkeit von 'PI1' in einen ganzzahligen Typ erscheint mir also ein wenig unpassend. –

+0

@HowardHinnant: Die Regeln für die Konvertierung von lvalue-to-rvalue unterscheiden sich subtil für ganzzahlige und nicht ganzzahlige Typen: (5.19 (2)) 'ein glvalue des Integral- oder Enumerationstyps, der sich auf ein nichtflüchtiges const-Objekt mit einem vorhergehenden bezieht Initialisierung, initialisiert mit einem konstanten Ausdruck 'vs.' einem glvalue des Literaltyps, der auf ein nichtflüchtiges Objekt verweist, das mit conexpr definiert wurde, oder das sich auf ein Unterobjekt eines solchen Objekts bezieht. Das ist nicht das Gleiche wie die implizite Umwandlung in 'int', die unterschiedliche Regeln hat. – rici

58

Kein Unterschied hier, aber es spielt eine Rolle, 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, aber es verspricht nicht zur Compile-Zeit initialisiert werden. s1 ist markiert constexpr, so ist es eine Konstante und, weil S 's Konstruktor auch constexpr markiert ist, wird es zur Kompilierzeit initialisiert werden.

Meistens zählt diese, wenn die Initialisierung zur Laufzeit zeitaufwendig sein würde, und Sie wollen, dass die Arbeit drücken auf die Compiler ab, wo es ist auch zeitaufwendig, aber langsam nicht die Ausführungszeit des kompilierten Programms nach unten

+3

Ich bin damit einverstanden: die Schlussfolgerung ich angekommen war, dass 'constexpr' zu einer Diagnose führen sollte die Kompilierung -Zeitberechnung des Objekts unmöglich sein. Was weniger klar ist, ist, ob eine Funktion * erwartet * ein konstanter Parameter zur Kompilierzeit ausgeführt werden könnte, sollte der Parameter als 'const' und nicht als 'constexpr' deklariert werden, dh würde 'constexpr int foo (S)' ausgeführt werden zur Kompilierzeit, wenn ich 'foo (s0)' rufe? –

+4

@MatthieuM: Ich bezweifle, ob 'foo (s0)' zur Kompilierzeit ausgeführt werden würde, aber man weiß nie: Ein Compiler darf solche Optimierungen durchführen. Sicher, weder gcc 4.7.2 noch clang 3.2 erlauben es mir, constexpr a = foo (s0) zu kompilieren. – rici

23

consExpr zeigt einen Wert an, der konstant ist und während der Kompilierung bekannt ist.
const zeigt einen Wert an, der nur konstant ist; Es ist nicht zwingend notwendig, 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, weil const Objekte nicht mit den Werten während der Kompilierung bekannt initialisiert werden müssen.

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 conexpr Objekte sind const, aber nicht alle const Objekte sind constexpr.

Wenn Sie möchten, dass Compiler garantieren, dass eine Variable einen Wert hat, der in Kontexten verwendet werden kann, die Kompilierzeitkonstanten erfordern, ist das Werkzeug, nach dem zu suchen, conexpr, nicht const.

+1

Ich mochte Ihre Erklärung sehr .. können Sie bitte kommentieren mehr auf Wo sind die Fälle, die wir brauchen, um Kompilierzeit Konstanten in realen Szenarien zu verwenden. –

+1

@MayukhSarkar Einfach Google _C++ warum constexpr_, z. http://stackoverflow.com/questions/4748083/when-should-you-use-constexpr-capability-in-c11 –

4

A consExpr symbolische Konstante muss einen Wert erhalten, der zur Kompilierzeit 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 
    // ... 
} 

Fälle zu behandeln, in denen der Wert einer „Variable“, die mit einem Wert initialisiert wird, der zum Zeitpunkt der Kompilierung nicht bekannt ist, aber ändert sich nie nach der Initialisierung C++ eine zweite Form von konstanten bietet (a const). Beispiel:

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:

  1. C++ 98 nicht constexpr haben, so dass die Leutekonst verwendet.
  2. Listenelement "Variablen", die keine konstanten Ausdrücke sind (ihr Wert ist zur Kompilierzeit nicht bekannt), aber Werte nach nicht ändern Initialisierung sind an sich sehr nützlich.
+10

Vielleicht sollten Sie erwähnt haben, dass der Text in Ihrer Antwort wörtlich aus "Programmierung: Prinzipien und Praxis mit C++ "von Stroustrup – Aky

Verwandte Themen