Welche Syntax ist am besten geeignet, um Konstanten zur Kompilierungszeit von ganzzahligen Typen wie den folgenden (im Funktions- und Klassenbereich) zu definieren?
static const int kMagic = 64; // (1)
constexpr int kMagic = 64; // (2)
(1)
Funktioniert auch für C++ 98/03-Compiler, stattdessen erfordert (2)
Mindestens C++ 11. Gibt es noch andere Unterschiede zwischen den beiden? Sollte das eine oder andere im modernen C++ - Code bevorzugt werden und warum?
[~ # ~] edit [~ # ~]
Ich habe diesen Beispielcode mit Godbolt's CE ausprobiert:
int main()
{
#define USE_STATIC_CONST
#ifdef USE_STATIC_CONST
static const int kOk = 0;
static const int kError = 1;
#else
constexpr int kOk = 0;
constexpr int kError = 1;
#endif
return kOk;
}
und für den Fall static const
ist dies die von GCC 6.2 generierte Assembly:
main::kOk:
.zero 4
main::kError:
.long 1
main:
Push rbp
mov rbp, rsp
mov eax, 0
pop rbp
ret
Andererseits ist es für constexpr
:
main:
Push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 0
mov DWORD PTR [rbp-8], 1
mov eax, 0
pop rbp
ret
Obwohl ich bei -O3
In beiden Fällen die gleiche (optimierte) Assembly bekomme:
main:
xor eax, eax
ret
EDIT # 2
Ich habe diesen einfachen Code ausprobiert (live auf Ideone) :
#include <iostream>
using namespace std;
int main() {
const int k1 = 10;
constexpr int k2 = 2*k1;
cout << k2 << '\n';
return 0;
}
dies zeigt, dass const int k1
zur Kompilierungszeit ausgewertet wird, da es zur Berechnung von constexpr int k2
verwendet wird.
Es scheint jedoch ein anderes Verhalten für double
s zu geben. Ich habe eine separate Frage dafür erstellt hier .
Solange es sich um die Deklaration von Kompilierzeitkonstanten von handelt skalar Ganzzahl- oder Aufzählungstypen, es gibt absolut keinen Unterschied zwischen der Verwendung von const
(static const
im Klassenbereich) oder constexpr
.
Beachten Sie, dass Compiler Objekte static const int
(Deklariert mit konstanten Initialisierern) in konstanten Ausdrücken unterstützen müssen, was bedeutet, dass sie keine andere Wahl haben, als solche Objekte als Konstanten zur Kompilierungszeit zu behandeln. Solange solche Objekte nicht verwendet werden, ist keine Definition erforderlich. Dies zeigt außerdem, dass sie nicht als Laufzeitwerte verwendet werden.
Außerdem verhindern die Regeln von konstante Initialisierung, dass lokale static const int
- Objekte dynamisch initialisiert werden, was bedeutet, dass es keine Leistungseinbußen gibt, wenn solche Objekte lokal deklariert werden. Darüber hinaus ist die Immunität von integralen static
Objekten gegenüber Ordnungsproblemen der statischen Initialisierung ein sehr wichtiges Merkmal der Sprache.
constexpr
ist eine Erweiterung und Verallgemeinerung des Konzepts, das ursprünglich in C++ durch const
mit einem konstanten Initialisierer implementiert wurde. Für Integer-Typen bietet constexpr
keine zusätzlichen Funktionen gegenüber den bereits durchgeführten const
. constexpr
führt einfach eine frühe Überprüfung der "Konstanz" des Initialisierers durch. Man könnte jedoch sagen, dass constexpr
eine Funktion ist, die speziell für diesen Zweck entwickelt wurde, damit sie stilistisch besser passt.
constexpr
Variable ist garantiert ein Wert zum Zeitpunkt der Kompilierung verfügbar. wohingegen static const
- Member oder const
-Variable entweder einen Kompilierzeitwert oder einen Laufzeitwert bedeuten können. Wenn Sie constexpr
eingeben, wird die Absicht eines Kompilierzeitwerts viel deutlicher ausgedrückt als bei const
.
Eine weitere Sache ist, dass in C++ 17 constexpr
statische Datenelementvariablen ebenfalls inline sind. Das heißt, Sie können die Out-of-Line-Definition von static constexpr
- Variablen weglassen, nicht jedoch von static const
.
Nachstehend finden Sie eine ausführlichere Erläuterung zu static const
Im Funktionsumfang.
Eine Variable static const
Im Funktionsumfang ist ziemlich gleich, hat jedoch keine automatische Speicherdauer, sondern eine statische Speicherdauer. Das heißt, es entspricht in gewisser Weise der Deklaration der Variablen als global, ist aber nur in der Funktion zugänglich.
Es ist richtig, dass eine static
-Variable beim ersten Aufruf der Funktion initialisiert wird. Da es sich jedoch auch um const
handelt, versucht der Compiler, den Wert in die Zeile einzufügen und die Variable vollständig zu optimieren. Wenn also in einer Funktion if der Wert zur Kompilierungszeit für diese bestimmte Variable bekannt ist, optimiert der Compiler ihn höchstwahrscheinlich.
Wenn der Wert zum Zeitpunkt der Kompilierung für ein static const
Im Funktionsumfang jedoch nicht bekannt ist, wird Ihre Funktion möglicherweise unbemerkt ausgeführt (a sehr kleines Bit) langsamer, da es den Wert zur Laufzeit initialisieren muss, wenn die Funktion zum ersten Mal aufgerufen wird. Außerdem muss bei jedem Funktionsaufruf überprüft werden, ob der Wert initialisiert wurde.
Das ist der Vorteil einer constexpr
-Variable. Wenn der Wert zur Kompilierungszeit nicht bekannt ist, handelt es sich um einen Kompilierungsfehler und nicht um eine langsamere Funktion. Wenn Sie dann beim Kompilieren keine Möglichkeit haben, den Wert Ihrer Variablen zu bestimmen, werden Sie vom Compiler darüber informiert, und Sie können etwas dagegen tun.