Namensräume
Varianten
Aktionen

Konstante Ausdrücke

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

Definiert einen Ausdruck, der zur Kompilierzeit ausgewertet werden kann.

Solche Ausdrücke können als Nicht-Typ-Template-Argumente, Array-Größen und in anderen Kontexten verwendet werden, die konstante Ausdrücke erfordern, z. B.

int n = 1;
std::array<int, n> a1;  // Error: “n” is not a constant expression
const int cn = 2;
std::array<int, cn> a2; // OK: “cn” is a constant expression

Inhalt

[bearbeiten] Definition

Ein Ausdruck, der zu einer der unten aufgeführten Kategorien von konstanten Ausdrücken gehört, ist ein konstanter Ausdruck.

C++98-Kategorien von konstanten Ausdrücken

Ganzzahliger konstanter Ausdruck (C++98)

An den folgenden Stellen verlangt C++ Ausdrücke, die zu einer ganzzahligen oder Aufzählungskonstante ausgewertet werden

Ein Ausdruck, der alle folgenden Bedingungen erfüllt, ist ein ganzzahliger konstanter Ausdruck 

  • Er bezieht sich nur auf die folgenden Entitäten
  • Literale von arithmetischen Typen
  • Aufzählungskonstantenelemente
  • Variablen oder statische Datenmember, die alle folgenden Bedingungen erfüllen
  • Sie sind const-qualifiziert.
  • Sie sind nicht volatile-qualifiziert.
  • Sie sind vom ganzzahligen oder Aufzählungstyp.
  • Sie werden mit konstanten Ausdrücken initialisiert.
  • Er verwendet keine Gleitkomma-Literale, es sei denn, sie werden explizit in ganzzahlige oder Aufzählungstypen konvertiert.
  • Er wendet keine Konvertierung auf nicht-ganzzahlige und nicht-Aufzählungstypen an.
  • Er verwendet keine der folgenden Entitäten, außer in den Operanden von sizeof
  • Funktion
  • Klassenobjekt
  • Zeiger
  • Referenz
  • Zuweisungsoperator
  • Inkrementierungsoperator
  • Dekrementierungsoperator
  • Funktionsaufrufoperator
  • Komma-Operator

Andere Kategorien von konstanten Ausdrücken

Andere Ausdrücke werden nur zu dem Zweck der konstanten Initialisierung als konstante Ausdrücke betrachtet. Ein solcher konstanter Ausdruck muss einer der folgenden Ausdrücke sein

  • ein Ausdruck, der zu einem Nullzeigerwert ausgewertet wird
  • ein Ausdruck, der zu einem Nullzeiger-auf-Member-Wert ausgewertet wird
  • ein arithmetischer konstanter Ausdruck
  • ein Adress-Konstanter Ausdruck
  • ein Referenz-Konstanter Ausdruck
  • ein Adress-Konstanter Ausdruck für einen vollständigen Objekttyp, zuzüglich oder abzüglich eines ganzzahligen konstanten Ausdrucks
  • ein Zeiger-auf-Member-Konstanter Ausdruck

Ein arithmetischer konstanter Ausdruck ist ein Ausdruck, der die Anforderungen an einen ganzzahligen konstanten Ausdruck erfüllt, mit den folgenden Ausnahmen

  • Gleitkomma-Literale können ohne explizite Konvertierung verwendet werden.
  • Konvertierungen in Gleitkommatypen können angewendet werden.

Ein Adress-Konstanter Ausdruck ist ein Ausdruck vom Zeigertyp, der alle folgenden Bedingungen erfüllt

  • explizit unter Verwendung des Adress-von-Operators
  • implizit unter Verwendung eines Nicht-Typ-Template-Parameters vom Zeigertyp
  • unter Verwendung eines Ausdrucks vom Array- oder Funktionstyp
  • Der Ausdruck ruft keine Funktion auf.
  • Der Ausdruck verwendet explizite Zeigerkonvertierungen (außer dynamic_cast) und die folgenden Operatoren, ohne auf das Ergebnisobjekt zuzugreifen
  • Indexoperator
  • Indirektionsoperator
  • Adress-von-Operator
  • Member-Zugriffsoperator
  • Wenn der Indexoperator verwendet wird, ist einer seiner Operanden ein ganzzahliger konstanter Ausdruck.

Ein Referenz-Konstanter Ausdruck ist ein Ausdruck vom Referenztyp, der alle folgenden Bedingungen erfüllt

  • Die Referenz bezeichnet ein Objekt mit statischer Speicher-Dauer, einen Nicht-Typ-Template-Parameter vom Referenztyp oder eine Funktion. Die Referenz bezeichnet kein Member oder keine Basisklasse eines Nicht-POD-Klassentyps.
  • Der Ausdruck ruft keine Funktion auf.
  • Der Ausdruck verwendet explizite Referenzkonvertierungen (außer dynamic_cast) und die folgenden Operatoren, ohne auf das Ergebnisobjekt zuzugreifen
  • Indexoperator
  • Indirektionsoperator
  • Adress-von-Operator
  • Member-Zugriffsoperator
  • Wenn der Indexoperator verwendet wird, ist einer seiner Operanden ein ganzzahliger konstanter Ausdruck.

Ein Zeiger-auf-Member-Konstanter Ausdruck ist ein Ausdruck vom Zeiger-auf-Member-Typ, bei dem der Zeiger durch Anwendung des Adress-von-Operators auf einen qualifizierten Bezeichner erstellt wird, optional vorangestellt durch eine explizite Zeiger-auf-Member-Konvertierung.

(bis C++11)

Die folgenden Ausdrücke werden kollektiv als konstante Ausdrücke bezeichnet 

  • die Adresse eines Objekts mit statischer Speicher-Dauer
  • die Adresse einer Funktion
  • ein Nullzeigerwert
(seit C++11)
(bis C++14)

Die folgenden Entitäten sind erlaubte Ergebnisse eines konstanten Ausdrucks 

  • temporäre Objekte mit statischer Speicher-Dauer
  • nicht-temporäre Objekte mit statischer Speicher-Dauer, deren Werte die unten aufgeführten Einschränkungen erfüllen
  • nicht-sofortige (seit C++20) Funktionen

Ein konstanter Ausdruck ist entweder ein glvalue Kern-Konstanter Ausdruck, der auf eine Entität verweist, die ein erlaubtes Ergebnis eines konstanten Ausdrucks ist, oder ein prvalue Kern-Konstanter Ausdruck, dessen Wert die folgenden Einschränkungen erfüllt

  • Wenn der Wert ein Objekt vom Klassentyp ist, verweist jedes nicht-statische Datenmember vom Referenztyp auf eine Entität, die ein erlaubtes Ergebnis eines konstanten Ausdrucks ist.
  • Wenn der Wert ein Objekt vom Skalar-Typ ist, hat er keinen unbestimmten Wert.
  • Wenn der Wert vom Zeigertyp ist, ist er einer der folgenden Werte
  • die Adresse eines Objekts mit statischer Speicher-Dauer
  • die Adresse nach dem Ende eines Objekts mit statischer Speicher-Dauer
  • die Adresse einer nicht-sofortigen(seit C++20) Funktion
  • ein Nullzeigerwert
  • Wenn der Wert vom Typ Zeiger-auf-Member-Funktion ist, bezeichnet er keine sofortige Funktion.
(seit C++20)
  • Wenn der Wert ein Objekt vom Klassen- oder Arraytyp ist, erfüllen alle Teilobjekte diese Einschränkungen für den Wert.
(seit C++14)
(bis C++26)

Ein konstanter Ausdruck ist entweder ein glvalue Kern-Konstanter Ausdruck, der auf ein Objekt oder eine nicht-sofortige Funktion verweist, oder ein prvalue Kern-Konstanter Ausdruck, dessen Wert die folgenden Einschränkungen erfüllt

(seit C++26)

Bei der Bestimmung, ob ein Ausdruck ein konstanter Ausdruck ist, wird angenommen, dass Copy Elision nicht durchgeführt wird.

Die C++98-Definition von konstanten Ausdrücken befindet sich vollständig in der Kollapsbox. Die folgende Beschreibung gilt für C++11 und spätere C++-Versionen.

[bearbeiten] Literal-Typ

Die folgenden Typen werden kollektiv als Literal-Typen bezeichnet 

  • möglicherweise cv-qualifiziertes void
  • Skalar-Typ
  • Referenz-Typ
  • ein Array vom Literal-Typ
  • möglicherweise cv-qualifizierter Klassentyp, der alle folgenden Bedingungen erfüllt
  • Er hat einen trivialen Destruktor(bis C++20)constexpr Destruktor(seit C++20).
  • Alle seine nicht-statischen Nicht-Varianten-Datenmember und Basisklassen sind von nicht-volatilen Literal-Typen.
  • Er ist einer der folgenden Typen
(seit C++17)
  • ein Aggregat-Union-Typ, der eine der folgenden Bedingungen erfüllt
  • Er hat kein Varianten-Member.
  • Er hat mindestens ein Varianten-Member vom nicht-volatilen Literal-Typ.
  • ein nicht-Union-Aggregat-Typ, und jeder seiner anonymen Union-Member erfüllt eine der folgenden Bedingungen
  • Er hat kein Varianten-Member.
  • Er hat mindestens ein Varianten-Member vom nicht-volatilen Literal-Typ.
  • ein Typ mit mindestens einem constexpr-Konstruktor (Template), der kein Kopier- oder Verschiebekonstruktor ist

Nur Objekte von Literal-Typen können innerhalb eines konstanten Ausdrucks erstellt werden.

[bearbeiten] Kern-Konstantausdruck

Ein Kern-Konstanter Ausdruck ist jeder Ausdruck, dessen Auswertung **nicht** einen der folgenden Sprachkonstrukte auswertet

Sprachkonstrukt     Version     Papier(e)
der this-Zeiger, außer in einer constexpr Funktion, die als Teil des Ausdrucks ausgewertet wird, oder wenn er in einem impliziten oder expliziten Klassenmember-Zugriffs-Ausdruck vorkommt N2235
ein Kontrollfluss, der durch eine Deklaration einer Blockvariable mit statischer oder Thread- Speicher-Dauer führt, die nicht in konstanten Ausdrücken verwendbar ist (seit C++23) P2242R3
  1. ein Funktionsaufrufausdruck, der eine Funktion (oder einen Konstruktor) aufruft, die/der nicht als constexpr deklariert ist
    constexpr int n = std::numeric_limits<int>::max(); // OK: max() is constexpr
    constexpr int m = std::time(nullptr); // Error: std::time() is not constexpr
  2. ein Funktionsaufruf an eine constexpr-Funktion, die deklariert, aber nicht definiert ist
  3. ein Funktionsaufruf an eine constexpr-Funktion/Konstruktor-Template-Instanziierung, bei der die Instanziierung die Anforderungen an eine constexpr Funktion/Konstruktor nicht erfüllt.
  4. ein Funktionsaufruf einer konstanten virtuellen Funktion, aufgerufen für ein Objekt, dessen dynamischer Typ constexpr-unbekannt ist
  5. ein Ausdruck, der die implementierungsdefinierten Grenzen überschreiten würde
  6. ein Ausdruck, dessen Auswertung zu irgendeiner Form von undefiniertem oder fehlerhaftem(seit C++26) Verhalten der Kernsprache führt, außer für jegliches potenzielle undefinierte Verhalten, das durch Standardattribute eingeführt wird.
    constexpr double d1 = 2.0 / 1.0; // OK
    constexpr double d2 = 2.0 / 0.0; // Error: not defined
    constexpr int n = std::numeric_limits<int>::max() + 1; // Error: overflow
    int x, y, z[30];
    constexpr auto e1 = &y - &x;        // Error: undefined
    constexpr auto e2 = &z[20] - &z[3]; // OK
    constexpr std::bitset<2> a; 
    constexpr bool b = a[2]; // UB, but unspecified if detected
  7. (bis C++17) ein Lambda-Ausdruck
  8. eine implizite Konvertierung von lvalue zu rvalue, es sei denn, sie wird angewendet auf...
    1. ein glvalue vom Typ (möglicherweise cv-qualifiziert) std::nullptr_t
    2. ein nicht-volatiles glvalue vom Literal-Typ, der ein Objekt bezeichnet, das in konstanten Ausdrücken verwendbar ist
      int main()
      {
          const std::size_t tabsize = 50;
          int tab[tabsize]; // OK: tabsize is a constant expression
                            // because tabsize is usable in constant expressions
                            // because it has const-qualified integral type, and
                            // its initializer is a constant initializer
       
          std::size_t n = 50;
          const std::size_t sz = n;
          int tab2[sz]; // Error: sz is not a constant expression
                        // because sz is not usable in constant expressions
                        // because its initializer was not a constant initializer
      }
    3. ein nicht-volatiles glvalue vom Literal-Typ, der auf ein nicht-volatiles Objekt verweist, dessen Lebensdauer innerhalb der Auswertung dieses Ausdrucks begann
  9. eine implizite Konvertierung von lvalue zu rvalue oder eine Modifikation, die auf einen nicht-aktiven Member einer Union oder sein Teilobjekt angewendet wird (auch wenn er eine gemeinsame Anfangssequenz mit dem aktiven Member teilt)
  10. eine implizite Konvertierung von lvalue zu rvalue für ein Objekt, dessen Wert unbestimmt ist
  11. ein Aufruf des impliziten Kopier-/Verschiebekonstruktors/-zuweisungsoperators für eine Union, deren aktiver Member veränderlich ist (falls vorhanden), wobei die Lebensdauer außerhalb der Auswertung dieses Ausdrucks beginnt
  12. (bis C++20) ein Zuweisungsausdruck, der den aktiven Member einer Union ändern würde
  13. Konvertierung von Zeiger auf void zu einem Zeiger-auf-Objekt-Typ T* es sei denn, der Zeiger enthält einen Nullzeigerwert oder zeigt auf ein Objekt, dessen Typ ähnlich zu T ist(seit C++26)
  14. dynamic_cast dessen Operand ein glvalue ist, das auf ein Objekt verweist, dessen dynamischer Typ constexpr-unbekannt ist(seit C++20)
  15. reinterpret_cast
  16. (bis C++20) Pseudo-Destruktoraufruf
  17. (bis C++14) ein Inkrementierungs- oder Dekrementierungsoperator
  18. (seit C++14) Modifikation eines Objekts, es sei denn, das Objekt hat einen nicht-volatilen Literal-Typ und seine Lebensdauer begann innerhalb der Auswertung des Ausdrucks
    constexpr int incr(int& n)
    {
        return ++n;
    }
     
    constexpr int g(int k)
    {
        constexpr int x = incr(k); // Error: incr(k) is not a core constant
                                   // expression because lifetime of k
                                   // began outside the expression incr(k)
        return x;
    }
     
    constexpr int h(int k)
    {
        int x = incr(k); // OK: x is not required to be initialized
                         // with a core constant expression
        return x;
    }
     
    constexpr int y = h(1); // OK: initializes y with the value 2
                            // h(1) is a core constant expression because
                            // the lifetime of k begins inside the expression h(1)
  19. (seit C++20) ein Destruktoraufruf oder Pseudo-Destruktoraufruf für ein Objekt, dessen Lebensdauer nicht innerhalb der Auswertung dieses Ausdrucks begann
  20. ein typeid-Ausdruck, angewendet auf ein glvalue vom polymorphen Typ und dieses glvalue verweist auf ein Objekt, dessen dynamischer Typ constexpr-unbekannt ist(seit C++20)
  21. ein new-Ausdruck, es sei denn, eine der folgenden Bedingungen ist erfüllt:(seit C++20)
    • Die ausgewählte Allocationsfunktion ist eine ersetzbare globale Allocationsfunktion und der zugewiesene Speicher wird innerhalb der Auswertung dieses Ausdrucks freigegeben.
    (seit C++20)
    • Die ausgewählte Allocationsfunktion ist eine nicht-allozierende Form mit einem zugewiesenen Typ T, und das Platzierungsargument erfüllt alle folgenden Bedingungen
    • Es verweist auf
    • ein Objekt, dessen Typ ähnlich zu T ist, wenn T kein Array-Typ ist, oder
    • das erste Element eines Objekts eines zu T ähnlichen Typs, wenn T ein Array-Typ ist.
    • Es verweist auf Speicher, dessen Lebensdauer innerhalb der Auswertung dieses Ausdrucks begann.
    (seit C++26)
  22. ein delete-Ausdruck, es sei denn, er gibt einen Speicherbereich frei, der innerhalb der Auswertung dieses Ausdrucks zugewiesen wurde(seit C++20)
  23. (seit C++20) Coroutinen: ein await-Ausdruck oder ein yield-Ausdruck
  24. (seit C++20) ein Drei-Wege-Vergleich, wenn das Ergebnis nicht spezifiziert ist
  25. ein Gleichheits- oder Vergleichsoperator, dessen Ergebnis nicht spezifiziert ist
  26. (bis C++14) ein Zuweisungs- oder zusammengesetzter Zuweisungsoperator
  27. (bis C++26) ein throw-Ausdruck
  28. (seit C++26) eine Konstruktion eines Ausnahmeobjekts, es sei denn, das Ausnahmeobjekt und alle seine impliziten Kopien, die durch Aufrufe von std::current_exception oder std::rethrow_exception erstellt wurden, werden innerhalb der Auswertung dieses Ausdrucks zerstört
    constexpr void check(int i)
    {
        if (i < 0)
            throw i;
    }
     
    constexpr bool is_ok(int i)
    {
        try {
            check(i);
        } catch (...) {
            return false;
        }
        return true;
    }
     
    constexpr bool always_throw()
    {
        throw 12;
        return true;
    }
     
    static_assert(is_ok(5)); // OK
    static_assert(!is_ok(-1)); // OK since C++26
    static_assert(always_throw()); // Error: uncaught exception
  29. eine asm-Deklaration
  30. ein Aufruf des Makros va_arg
  31. eine goto-Anweisung
  32. ein dynamic_cast- oder typeid-Ausdruck oder new-Ausdruck(seit C++26), der eine Ausnahme auslösen würde wenn keine Definition des Ausnahmetyps erreichbar ist(seit C++26)
  33. innerhalb eines Lambda-Ausdrucks eine Referenz auf this oder auf eine außerhalb dieses Lambda definierten Variable, wenn diese Referenz eine ODR-Nutzung wäre
    void g()
    {
        const int n = 0;
     
        constexpr int j = *&n; // OK: outside of a lambda-expression
     
        [=]
        {
            constexpr int i = n;   // OK: 'n' is not odr-used and not captured here.
            constexpr int j = *&n; // Ill-formed: '&n' would be an odr-use of 'n'.
        };
    }

    beachten Sie, dass, wenn die ODR-Nutzung in einem Funktionsaufruf einer Closure stattfindet, sie sich nicht auf this oder eine umschließende Variable bezieht, da sie stattdessen auf ein Datenmember der Closure zugreift

    // OK: 'v' & 'm' are odr-used but do not occur in a constant-expression
    // within the nested lambda
    auto monad = [](auto v){ return [=]{ return v; }; };
    auto bind = [](auto m){ return [=](auto fvm){ return fvm(m()); }; };
     
    // OK to have captures to automatic objects created during constant expression evaluation.
    static_assert(bind(monad(2))(monad)() == monad(2)());
    (seit C++17)

[bearbeiten] Zusätzliche Anforderungen

Auch wenn ein Ausdruck E nichts von dem oben Genannten auswertet, ist es implementierungsabhängig, ob E ein Kern-Konstanter Ausdruck ist, wenn die Auswertung von E zu laufzeit-undefiniertem Verhalten führen würde.

Auch wenn ein Ausdruck E nichts von dem oben Genannten auswertet, ist es unspezifiziert, ob E ein Kern-Konstanter Ausdruck ist, wenn die Auswertung von E eines der folgenden Elemente auswertet

Für die Zwecke der Bestimmung, ob ein Ausdruck ein Kern-Konstanter Ausdruck ist, wird die Auswertung des Körpers einer Member-Funktion von std::allocator<T> ignoriert, wenn T ein Literal-Typ ist.

Für die Zwecke der Bestimmung, ob ein Ausdruck ein Kern-Konstanter Ausdruck ist, wird die Auswertung eines Aufrufs eines trivialen Kopier-/Verschiebekonstruktors oder Kopier-/Zuweisungsoperators einer Union als Kopieren/Verschieben des aktiven Members der Union betrachtet, falls vorhanden.

Für die Zwecke der Bestimmung, ob ein Ausdruck ein Kern-Konstanter Ausdruck ist, hat die Auswertung eines Bezeichnerausdrucks, der ein strukturiertes Binding bd bezeichnet, die folgenden Semantiken

  • Wenn bd ein lvalue ist, das auf das Objekt verweist, das an eine erfundene Referenz ref gebunden ist, ist das Verhalten so, als ob ref nominiert wäre.
  • Andernfalls, wenn bd ein Array-Element bezeichnet, ist das Verhalten dasselbe wie die Auswertung von e[i], wobei e der Name der Variablen ist, die aus dem Initialisierer der strukturierten Binding-Deklaration initialisiert wurde, und i der Index des Elements ist, auf das bd verweist.
  • Andernfalls, wenn bd ein Klassenmember bezeichnet, ist das Verhalten dasselbe wie die Auswertung von e.m, wobei e der Name der Variablen ist, die aus dem Initialisierer der strukturierten Binding-Deklaration initialisiert wurde, und m der Name des Members ist, auf den bd verweist.
(seit C++26)

Während der Auswertung des Ausdrucks als Kern-Konstanter Ausdruck werden alle Bezeichnerausdrücke und Verwendungen von *this, die auf ein Objekt oder eine Referenz verweisen, deren Lebensdauer außerhalb der Auswertung des Ausdrucks begann, so behandelt, als würden sie auf eine spezifische Instanz dieses Objekts oder dieser Referenz verweisen, deren Lebensdauer und die aller Teilobjekte (einschließlich aller Union-Member) die gesamte konstante Auswertung umfasst.

  • Für ein solches Objekt das nicht in konstanten Ausdrücken verwendbar ist(seit C++20) ist der dynamische Typ des Objekts constexpr-unbekannt.
  • Für eine solche Referenz die nicht in konstanten Ausdrücken verwendbar ist(seit C++20) wird die Referenz so behandelt, als wäre sie an ein unspezifiziertes Objekt des Referenztyps gebunden, dessen Lebensdauer und die aller Teilobjekte die gesamte konstante Auswertung umfasst und dessen dynamischer Typ constexpr-unbekannt ist.

[bearbeiten] Ganzzahliger konstanter Ausdruck

Ganzzahliger konstanter Ausdruck ist ein Ausdruck vom ganzzahligen oder nicht-gesteckten Aufzählungstyp, der implizit in einen prvalue konvertiert wird, wobei der konvertierte Ausdruck ein Kern-Konstanter Ausdruck ist.

Wenn ein Ausdruck vom Klassentyp dort verwendet wird, wo ein ganzzahliger konstanter Ausdruck erwartet wird, wird der Ausdruck kontextuell implizit in einen ganzzahligen oder nicht-gesteckten Aufzählungstyp konvertiert.

[bearbeiten] Konvertierter konstanter Ausdruck

Ein konvertierter konstanter Ausdruck vom Typ T ist ein Ausdruck, der implizit in den Typ T konvertiert wird, wobei der konvertierte Ausdruck ein konstanter Ausdruck ist und die implizite Konvertierungssequenz nur Folgendes enthält

(seit C++17)

Und wenn eine Referenzbindung stattfindet, kann sie nur eine direkte Bindung sein.

Die folgenden Kontexte erfordern einen konvertierten konstanten Ausdruck

(seit C++14)
(seit C++26)

Ein kontextuell konvertierter konstanter Ausdruck vom Typ bool ist ein Ausdruck, der kontextuell in bool konvertiert wird, wobei der konvertierte Ausdruck ein konstanter Ausdruck ist und die Konvertierungssequenz nur die oben genannten Konvertierungen enthält.

Die folgenden Kontexte erfordern einen kontextuell konvertierten konstanten Ausdruck vom Typ bool

(bis C++23)
(seit C++17)
(bis C++23)
(seit C++20)


Bestandteile

Die Bestandteil-Werte eines Objekts obj werden wie folgt definiert

  • Wenn obj einen Skalar-Typ hat, ist der Bestandteil-Wert der Wert von obj.
  • Andernfalls sind die Bestandteil-Werte die Bestandteil-Werte von direkten Teilobjekten von obj, außer inaktiven Union-Mitgliedern.

Die Bestandteil-Referenzen eines Objekts obj umfassen die folgenden Referenzen

  • direkte Member von obj, die einen Referenztyp haben
  • die Bestandteil-Referenzen von direkten Teilobjekten von obj, außer inaktiven Union-Mitgliedern

Die Bestandteil-Werte und Bestandteil-Referenzen einer Variablen var werden wie folgt definiert

  • Wenn var ein Objekt deklariert, sind die Bestandteil-Werte und Referenzen die Bestandteil-Werte und Referenzen dieses Objekts.
  • Wenn var eine Referenz deklariert, ist die Bestandteil-Referenz diese Referenz.

Für jede Bestandteilreferenz ref einer Variablen var, falls ref an ein temporäres Objekt oder ein Teilobjekt davon gebunden ist, dessen Lebensdauer bis zu der von ref verlängert wird, sind die Bestandteilwerte und -referenzen dieses temporären Objekts rekursiv ebenfalls Bestandteilwerte und -referenzen von var.

Constexpr-darstellbare Entitäten

Objekte mit statischer Speicherungsdauer sind zu jedem Zeitpunkt im Programm constexpr-referenzierbar.

Ein Objekt obj mit automatischer Speicherungsdauer ist von einem Punkt P constexpr-referenzierbar, wenn der kleinste Scope, der die Variable var einschließt, und der kleinste Scope, der P einschließt, derselbe Funktionsparameterrbereich sind, der nicht mit der Parameterliste eines requires-Ausdrucks assoziiert ist, wobei var die Variable ist, die dem vollständigen Objekt von obj entspricht, oder die Variable, auf deren Lebensdauer die von obj verlängert wurde.

Ein Objekt oder eine Referenz x ist an einem Punkt P constexpr-darstellbar, wenn alle folgenden Bedingungen erfüllt sind

  • Für jeden Bestandteilwert von x, der auf ein Objekt obj zeigt, ist obj von P constexpr-referenzierbar.
  • Für jeden Bestandteilwert von x, der über ein Objekt obj hinauszeigt, ist obj von P constexpr-referenzierbar.
  • Für jede Bestandteilreferenz von x, die sich auf ein Objekt obj bezieht, ist obj von P constexpr-referenzierbar.
(seit C++26)

Konstant-initialisierte Entitäten

Eine Variable oder ein temporäres Objekt obj ist konstant-initialisiert, wenn alle folgenden Bedingungen erfüllt sind

  • Entweder hat es einen Initialisierer, oder sein Typ ist const-default-konstruierbar.
  • Der vollständige Ausdruck seiner Initialisierung ist ein konstanter Ausdruck im Kontext der Anforderung eines konstanten Ausdrucks, mit der Ausnahme, dass, wenn obj ein Objekt ist, dieser vollständige Ausdruck auch constexpr-Konstruktoren für obj und seine Teilobjekte aufrufen darf, selbst wenn diese Objekte von Klassen des Typs Nicht-Literal sind.
(bis C++26)

Eine Variable var ist konstant-initialisierbar, wenn alle folgenden Bedingungen erfüllt sind

  • Der vollständige Ausdruck ihrer Initialisierung ist ein konstanter Ausdruck im Kontext der Anforderung eines konstanten Ausdrucks, wobei alle Vertragszusicherungen die Auswertungssemantik "ignorieren" verwenden.
  • Unmittelbar nach der initialisierenden Deklaration von var ist das durch var deklarierte Objekt oder die Referenz constexpr-darstellbar.
  • Wenn das durch var deklarierte Objekt oder die Referenz x eine statische oder Thread-Speicherungsdauer hat, ist x am nächstgelegenen Punkt, dessen unmittelbarer Scope ein Namespace-Scope ist, der der initialisierenden Deklaration von var folgt, constexpr-darstellbar.

Eine konstant-initialisierbare Variable ist konstant-initialisiert, wenn entweder sie einen Initialisierer hat oder ihr Typ const-default-konstruierbar ist.

(seit C++26)

Verwendbar in konstanten Ausdrücken

Eine Variable ist potenziell konstant, wenn sie eine constexpr-Variable ist oder einen Referenz- oder nicht-flüchtigen, const-qualifizierten ganzzahligen oder Enumerationstyp hat.

Eine konstant-initialisierte, potenziell konstante Variable var ist an einem Punkt P in konstanten Ausdrücken verwendbar, wenn die initialisierende Deklaration D von var von P erreichbar ist und eine der folgenden Bedingungen erfüllt ist

  • var ist eine constexpr-Variable.
  • var wird nicht mit einem TU-lokalen Wert initialisiert.
  • P befindet sich in derselben Übersetzungseinheit wie D.

Ein Objekt oder eine Referenz ist an einem Punkt P in konstanten Ausdrücken verwendbar, wenn es eine der folgenden Entitäten ist

  • eine Variable, die an P in konstanten Ausdrücken verwendbar ist
  • ein temporäres Objekt eines nicht-flüchtigen, const-qualifizierten Literal-Typs, dessen Lebensdauer bis zu einer Variable verlängert wird, die an P in konstanten Ausdrücken verwendbar ist
  • ein Template-Parameter-Objekt
  • ein String-Literal-Objekt
  • ein nicht-veränderliches Teilobjekt von einem der obigen
  • eine Referenz-Mitgliedskomponente von einem der obigen
(bis C++26)

Ein Objekt oder eine Referenz ist an einem Punkt P potenziell in konstanten Ausdrücken verwendbar, wenn es eine der folgenden Entitäten ist

  • eine Variable, die an P in konstanten Ausdrücken verwendbar ist
  • ein temporäres Objekt eines nicht-flüchtigen, const-qualifizierten Literal-Typs, dessen Lebensdauer bis zu einer Variable verlängert wird, die an P in konstanten Ausdrücken verwendbar ist
  • ein Template-Parameter-Objekt
  • ein String-Literal-Objekt
  • ein nicht-veränderliches Teilobjekt von einem der obigen
  • eine Referenz-Mitgliedskomponente von einem der obigen

Ein Objekt oder eine Referenz ist an einem Punkt P in konstanten Ausdrücken verwendbar, wenn es ein Objekt oder eine Referenz ist, die an P potenziell in konstanten Ausdrücken verwendbar ist und an P constexpr-darstellbar ist.

(seit C++26)

Manifest eindeutig konstant ausgewertete Ausdrücke

Die folgenden Ausdrücke (einschließlich Konvertierungen zum Zieltyp) sind manifest eindeutig konstant ausgewertet 

Ob eine Auswertung in einem manifest eindeutig konstant ausgewerteten Kontext stattfindet, kann mit std::is_constant_evaluated und if consteval(seit C++23) erkannt werden.

(seit C++20)

[edit] Funktionen und Variablen für die konstante Auswertung erforderlich

Folgende Ausdrücke oder Konvertierungen sind potenziell konstant ausgewertet

Eine Funktion ist für die konstante Auswertung erforderlich, wenn sie eine constexpr-Funktion ist und von einem Ausdruck genannt wird, der potenziell konstant ausgewertet wird.

Eine Variable ist für die konstante Auswertung erforderlich, wenn sie entweder eine constexpr-Variable ist oder einen nicht-flüchtigen, const-qualifizierten ganzzahligen Typ oder einen Referenztyp hat und der Bezeichnerausdruck, der sie bezeichnet, potenziell konstant ausgewertet wird.

Die Definition einer abgeleiteten Funktion und die Instanziierung einer Funktionsvorlagenspezialisierung oder einer Variablentemplatespezialisierung(seit C++14) wird ausgelöst, wenn die Funktion oder Variable(seit C++14) für die konstante Auswertung erforderlich ist.

[edit] Konstanter Teilausdruck

Ein konstanter Teilausdruck ist ein Ausdruck, dessen Auswertung als Teilausdruck eines Ausdrucks e e nicht daran hindern würde, ein Kernkonstantenausdruck zu sein, wobei e keiner der folgenden Ausdrücke ist

(seit C++20)

[edit] Hinweise

Feature-Testmakro Wert Std Feature
__cpp_constexpr_in_decltype 201711L (C++20)
(DR11)
Generierung von Funktions- und Variablendefinitionen, wenn sie gemäß für die konstante Auswertung erforderlich sind
__cpp_constexpr_dynamic_alloc 201907L (C++20) Operationen für dynamische Speicherverwaltung in constexpr-Funktionen
__cpp_constexpr 202306L (C++26) constexpr-Cast von void*: Richtung Typ-Erasure
202406L (C++26) constexpr Placement new und new[]
__cpp_constexpr_exceptions 202411L (C++26) constexpr-Ausnahmen

[edit] Beispiel

[edit] Fehlerberichte

Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 94 C++98 arithmetische konstante Ausdrücke konnten nicht
Variablen und statische Datenmember involvieren
sie können es
CWG 366 C++98 Ausdrücke, die String-Literale involvieren
konnten integrale konstante Ausdrücke sein
sie sind es nicht
CWG 457 C++98 Ausdrücke, die volatile Variablen involvieren
konnten integrale konstante Ausdrücke sein
sie sind es nicht
CWG 1293 C++11 es war unklar, ob String-Literale
in konstanten Ausdrücken verwendbar sind
sie sind verwendbar
CWG 1311 C++11 volatile lvalues konnten in konstanten Ausdrücken verwendet werden verboten
CWG 1312 C++11 reinterpret_cast ist in konstanten Ausdrücken verboten,
aber das Casting zu und von void* konnte den gleichen Effekt erzielen
verbotene Konvertierungen
von Typ cv void* zu
einem Pointer-to-Object-Typ
CWG 1313 C++11 undefiniertes Verhalten war erlaubt;
die gesamte Pointer-Subtraktion war verboten
UB verboten; dasselbe Array
Pointer-Subtraktion OK
CWG 1405 C++11 für Objekte, die in konstanten Ausdrücken verwendbar sind,
waren ihre veränderlichen Teilobjekte ebenfalls verwendbar
sie sind nicht verwendbar
CWG 1454 C++11 Weitergabe von Konstanten über constexpr-Funktionen
mittels Referenzen war nicht erlaubt
erlaubt
CWG 1455 C++11 konvertierte konstante Ausdrücke konnten nur prvalues sein können lvalues sein
CWG 1456 C++11 ein Adress-Konstantenausdruck konnte nicht
die Adresse eines Arrays über das Ende hinaus bezeichnen
erlaubt
CWG 1535 C++11 ein typeid-Ausdruck, dessen Operand ein
polymorphes Klassentyp ist, war kein Kernkonstantenausdruck
selbst wenn keine Laufzeitprüfung beteiligt ist
die Operandeneinschränkung
ist auf glvalues von
polymorphen Klassentypen beschränkt
CWG 1581 C++11 für die konstante Auswertung benötigte Funktionen
mussten nicht definiert oder instanziiert werden
Gefordert
CWG 1613 C++11 Kernkonstantenausdrücke konnten beliebige
ODR-verwendete Referenzen innerhalb von Lambda-Ausdrücken auswerten
einige Referenzen konnten
nicht ausgewertet werden
CWG 1694 C++11 die Bindung des Wertes eines Temporärs an eine Referenz mit statischer Speicherungsdauer
war ein konstanter Ausdruck
sie ist kein
Konstanter Ausdruck
CWG 1872 C++11 Kernkonstantenausdrücke konnten constexpr-Funktionsvorlageninstanziierungen aufrufen
die die Anforderungen für constexpr-Funktionen nicht erfüllen
solche Instanziierungen
können nicht aufgerufen werden
CWG 1952 C++11 undefinierte Standardbibliotheksverhaltensweisen
mussten diagnostiziert werden
unklar, ob
sie diagnostiziert werden
CWG 2022 C++98 die Bestimmung des konstanten Ausdrucks könnte
davon abhängen, ob Copy-Elision durchgeführt wird
angenommen, dass Copy-Elision
immer durchgeführt wird
CWG 2126 C++11 konstant-initialisierte, lebensdauer-erweiterte temporäre Objekte von const-
qualifizierten Literal-Typen waren nicht in konstanten Ausdrücken verwendbar
verwendbar
CWG 2129 C++11 Ganzzahl-Literale waren keine konstanten Ausdrücke sie sind
CWG 2167 C++11 Nicht-Mitglieds-Referenzen lokal zu einer Auswertung
machten die Auswertung nicht-constexpr
Nicht-Mitglieds-
Referenzen erlaubt
CWG 2278 C++98 die Lösung von CWG-Problem 2022 war nicht implementierbar angenommen, dass Copy-Elision
wird nie durchgeführt
CWG 2299 C++14 es war unklar, ob Makros in <cstdarg>
in der konstanten Auswertung verwendet werden können
va_arg verboten,
va_start unbestimmt
CWG 2400 C++11 das Aufrufen einer constexpr virtuellen Funktion für ein Objekt, das nicht verwendbar ist
in konstanten Ausdrücken und dessen Lebensdauer außerhalb des Ausdrucks begann, der den Aufruf enthält, konnte ein konstanter Ausdruck sein
Ausdruck, der den Aufruf enthält, könnte ein konstanter Ausdruck sein
sie ist kein
Konstanter Ausdruck
CWG 2490 C++20 (Pseudo-)Destruktoraufrufe hatten keine
Beschränkungen in der konstanten Auswertung
Beschränkung hinzugefügt
CWG 2552 C++23 bei der Auswertung eines Kernkonstantenausdrucks konnte der Kontrollfluss
nicht durch die Deklaration einer Nicht-Block-Variable fließen
es kann
CWG 2558 C++11 ein unbestimmter Wert konnte ein konstanter Ausdruck sein kein konstanter Ausdruck
CWG 2647 C++20 Variablen vom Typ volatile-qualifiziert konnten potenziell konstant sein sie sind es nicht
CWG 2763 C++11 die Verletzung von [[noreturn]] musste nicht
während der konstanten Auswertung erkannt werden
Gefordert
CWG 2851 C++11 konvertierte konstante Ausdrücke erlaubten
keine Gleitkomma-Konvertierungen
erlaubt nicht-einschränkende
Gleitkomma-Konvertierungen
CWG 2907 C++11 Kernkonstantenausdrücke konnten keine
lvalue-zu-rvalue-Konvertierungen auf std::nullptr_t glvalues anwenden
kann solche anwenden
Konvertierungen
CWG 2909 C++20 eine Variable ohne Initialisierer konnte nur
konstant-initialisiert werden, wenn ihre Default-Initialisierung
zu einer tatsächlichen Initialisierung führt
kann nur konstant-
initialisiert werden, wenn ihr Typ
const-default-konstruierbar ist
CWG 2924 C++11
C++23
es war unbestimmt, ob ein Ausdruck, der
die Constraints von [[noreturn]] (C++11) oder
[[assume]] (C++23) verletzt, ein Kernkonstantenausdruck ist
sie ist
implementierungsdefiniert
P2280R4 C++11 die Auswertung eines Ausdrucks, der einen Bezeichnerausdruck
oder *this enthält, der sich auf ein Objekt oder eine Referenz bezieht, deren Lebensdauer
außerhalb dieser Auswertung begonnen hat, ist kein konstanter Ausdruck
es kann ein
Konstanter Ausdruck

[edit] Siehe auch

constexpr-Spezifizierer(C++11) gibt an, dass der Wert einer Variablen oder Funktion zur Kompilierzeit berechnet werden kann[edit]
(C++11)(in C++17 veraltet)(in C++20 entfernt)
prüft, ob ein Typ ein Literal-Typ ist
(Klassenvorlage) [bearbeiten]
C-Dokumentation für Konstante Ausdrücke