webentwicklung-frage-antwort-db.com.de

constexpr vs. static const: Welches bevorzugen?

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 doubles zu geben. Ich habe eine separate Frage dafür erstellt hier .

36
Mr.C64

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.

17
AnT

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.

32