constexpr-Spezifizierer (seit C++11)
constexpr- gibt an, dass der Wert einer Variablen, eines strukturieren Bindungselements(seit C++26) oder einer Funktion in Konstanten Ausdrücken vorkommen kann
Inhalt |
[bearbeiten] Erklärung
Der constexpr-Spezifizierer deklariert, dass es möglich ist, den Wert der Entitäten zur Kompilierzeit auszuwerten. Solche Entitäten können dann dort verwendet werden, wo nur Konstante Ausdrücke zur Kompilierzeit zulässig sind (vorausgesetzt, es werden entsprechende Funktionsargumente angegeben).
Ein constexpr-Spezifizierer, der in einer Objektdeklaration oder einer nicht-statischen Memberfunktion(bis C++14) verwendet wird, impliziert const.
Ein constexpr-Spezifizierer, der in der ersten Deklaration einer Funktion oder eines statischen Datenmembers(seit C++17) verwendet wird, impliziert inline. Wenn eine Deklaration einer Funktion oder Funktionstemplate einen constexpr-Spezifizierer enthält, müssen alle Deklarationen diesen Spezifizierer enthalten.
[bearbeiten] constexpr-Variable
Eine Variable oder ein Variablentemplate(seit C++14) kann als constexpr deklariert werden, wenn alle folgenden Bedingungen erfüllt sind:
- Die Deklaration ist eine Definition.
- Sie hat einen Literaltyp.
- Sie wird (durch die Deklaration) initialisiert.
|
(bis C++26) |
|
(seit C++26) |
|
Wenn eine constexpr-Variable nicht übersetzungseinheit-lokal ist, sollte sie nicht so initialisiert werden, dass sie auf eine übersetzungseinheit-lokale Entität verweist, die in konstanten Ausdrücken verwendbar ist, noch sollte sie ein Unterobjekt haben, das auf eine solche Entität verweist. Eine solche Initialisierung ist in einer Modul-Interface-Einheit (außerhalb ihres privaten Modul-Fragments, falls vorhanden) oder einer Modulpartition nicht zulässig und in jedem anderen Kontext veraltet. |
(seit C++20) |
[bearbeiten] constexpr-Funktion
Eine Funktion oder ein Funktionstemplate kann als constexpr deklariert werden.
Eine Funktion ist constexpr-geeignet, wenn alle folgenden Bedingungen erfüllt sind:
- Wenn es sich um einen Konstruktor oder Destruktor(seit C++20) handelt, hat seine Klasse keine virtuelle Basisklasse.
|
(bis C++20) |
|
(bis C++23) |
|
(seit C++20) |
|
(bis C++14) | ||
|
(seit C++14) (bis C++23) |
Mit Ausnahme von instanziierten constexpr-Funktionen müssen nicht-generische, nicht-templatische constexpr-Funktionen constexpr-geeignet sein.
|
Wenn für eine nicht-Konstruktor- constexpr-Funktion, die weder defaultet noch eine Template-Instanziierung ist, keine Argumentwerte existieren, sodass eine Ausführung der Funktion ein ausgewerteter Teil eines Kern-Konstanten Ausdrucks sein könnte, ist das Programm ill-formed, keine Diagnose erforderlich. Wenn für eine templatisierte constexpr-Funktion keine Spezialisierung der Funktions- oder Klassentemplate die templatisierte Funktion constexpr-geeignet macht, wenn sie als nicht-templatisierte Funktion betrachtet wird, ist das Programm ill-formed, keine Diagnose erforderlich. |
(bis C++23) |
Eine Aufrufung einer constexpr-Funktion in einem gegebenen Kontext erzeugt das gleiche Ergebnis wie die Aufrufung einer äquivalenten, nicht-constexpr-Funktion im gleichen Kontext in jeder Hinsicht, mit folgenden Ausnahmen:
- Eine Aufrufung einer constexpr-Funktion kann in einem Konstanten Ausdruck vorkommen.
- Kopierelision wird in einem konstanten Ausdruck nicht durchgeführt.
[bearbeiten] constexpr-Konstruktor
Zusätzlich zu den Anforderungen von constexpr-Funktionen muss ein Konstruktor alle folgenden Bedingungen erfüllen, um constexpr-geeignet zu sein:
|
(bis C++23) |
- Die Klasse hat keine virtuelle Basisklasse.
|
Wenn für einen constexpr-Konstruktor, der weder defaultet noch eine Template-Instanziierung ist, keine Argumentwerte existieren, sodass eine Aufrufung der Funktion ein ausgewerteter Teil des Initialisierungs-vollständigen Ausdrucks eines Objekts sein könnte, das konstanten Ausdrücken unterliegt, ist das Programm ill-formed, keine Diagnose erforderlich. |
(bis C++23) |
[bearbeiten] constexpr-Destruktor
|
Destruktoren können nicht constexpr sein, aber ein trivialer Destruktor kann implizit in konstanten Ausdrücken aufgerufen werden. |
(bis C++20) | ||
|
Zusätzlich zu den Anforderungen von constexpr-Funktionen muss ein Destruktor alle folgenden Bedingungen erfüllen, um constexpr-geeignet zu sein:
|
(seit C++20) |
[bearbeiten] Hinweise
|
Da der constexpr int f(); constexpr bool b1 = noexcept(f()); // false, undefined constexpr function constexpr int f() { return 0; } constexpr bool b2 = noexcept(f()); // true, f() is a constant expression |
(bis C++17) |
|
Es ist möglich, eine constexpr-Funktion zu schreiben, deren Aufrufung niemals die Anforderungen eines Kern-Konstanten Ausdrucks erfüllen kann. void f(int& i) // not a constexpr function { i = 0; } constexpr void g(int& i) // well-formed since C++23 { f(i); // unconditionally calls f, cannot be a constant expression } |
(seit C++23) |
Constexpr-Konstruktoren sind für Klassen zulässig, die keine Literaltypen sind. Zum Beispiel ist der Standardkonstruktor von std::shared_ptr constexpr, was eine konstante Initialisierung ermöglicht.
Referenzvariablen können als constexpr deklariert werden (ihre Initialisierer müssen Referenz-Konstante Ausdrücke sein).
static constexpr int const& x = 42; // constexpr reference to a const int object // (the object has static storage duration // due to life extension by a static reference)
|
Obwohl try-Blöcke und Inline-Assembler in constexpr-Funktionen zulässig sind, ist das Werfen von Ausnahmen, die nicht abgefangen werden(seit C++26) oder die Ausführung der Assembler-Anweisungen in einem konstanten Ausdruck weiterhin nicht zulässig. Wenn eine Variable eine konstante Zerstörung hat, muss kein Maschinencode generiert werden, um ihren Destruktor aufzurufen, auch wenn ihr Destruktor nicht trivial ist. Eine nicht-Lambda-, nicht-Spezialmember- und nicht-templated constexpr-Funktion kann nicht implizit zu einer unmittelbaren Funktion werden. Benutzer müssen sie explizit als consteval markieren, um eine solche beabsichtigte Funktionsdefinition korrekt zu machen. |
(seit C++20) |
| Feature-Testmakro | Wert | Std | Feature |
|---|---|---|---|
__cpp_constexpr |
200704L |
(C++11) | constexpr |
201304L |
(C++14) | Entspannte constexpr-, nicht-const constexpr-Methoden | |
201603L |
(C++17) | Constexpr-Lambda | |
201907L |
(C++20) | Triviale Default-Initialisierung und asm-Deklaration in constexpr-Funktionen | |
202002L |
(C++20) | Änderung des aktiven Mitglieds einer Union bei konstanter Auswertung | |
202110L |
(C++23) | Nicht-literale Variablen, Labels und goto-Anweisungen in constexpr-Funktionen | |
202207L |
(C++23) | Lockerung einiger constexpr-Beschränkungen | |
202211L |
(C++23) | Zulassung von static constexpr-Variablen in constexpr-Funktionen | |
202306L |
(C++26) | Constexpr-Cast von void*: auf dem Weg zu constexpr Typ-Erasure | |
__cpp_constexpr_in_decltype |
201711L |
(C++11) (DR) |
Generierung von Funktions- und Variablendefinitionen, wenn sie für die konstante Auswertung benötigt werden |
__cpp_constexpr_dynamic_alloc |
201907L |
(C++20) | Operationen für dynamische Speicherverwaltung in constexpr-Funktionen |
[bearbeiten] Schlüsselwörter
[bearbeiten] Beispiel
Definiert C++11/14 constexpr-Funktionen, die Fakultäten berechnen; definiert einen Literaltyp, der String-Literale erweitert
#include <iostream> #include <stdexcept> // C++11 constexpr functions use recursion rather than iteration constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n - 1)); } // C++14 constexpr functions may use local variables and loops #if __cplusplus >= 201402L constexpr int factorial_cxx14(int n) { int res = 1; while (n > 1) res *= n--; return res; } #endif // C++14 // A literal class class conststr { const char* p; std::size_t sz; public: template<std::size_t N> constexpr conststr(const char(&a)[N]): p(a), sz(N - 1) {} // constexpr functions signal errors by throwing exceptions // in C++11, they must do so from the conditional operator ?: constexpr char operator[](std::size_t n) const { return n < sz ? p[n] : throw std::out_of_range(""); } constexpr std::size_t size() const { return sz; } }; // C++11 constexpr functions had to put everything in a single return statement // (C++14 does not have that requirement) constexpr std::size_t countlower(conststr s, std::size_t n = 0, std::size_t c = 0) { return n == s.size() ? c : 'a' <= s[n] && s[n] <= 'z' ? countlower(s, n + 1, c + 1) : countlower(s, n + 1, c); } // An output function that requires a compile-time constant, for testing template<int n> struct constN { constN() { std::cout << n << '\n'; } }; int main() { std::cout << "4! = "; constN<factorial(4)> out1; // computed at compile time volatile int k = 8; // disallow optimization using volatile std::cout << k << "! = " << factorial(k) << '\n'; // computed at run time std::cout << "The number of lowercase letters in \"Hello, world!\" is "; constN<countlower("Hello, world!")> out2; // implicitly converted to conststr constexpr int a[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; constexpr int length_a = sizeof a / sizeof(int); // std::size(a) in C++17, // std::ssize(a) in C++20 std::cout << "Array of length " << length_a << " has elements: "; for (int i = 0; i < length_a; ++i) std::cout << a[i] << ' '; std::cout << '\n'; }
Ausgabe
4! = 24 8! = 40320 The number of lowercase letters in "Hello, world!" is 9 Array of length 12 has elements: 0 1 2 3 4 5 6 7 8 0 0 0
[bearbeiten] 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 1358 | C++11 | templated constexpr functions also needed to have at least one valid argument value |
no need |
| CWG 1359 | C++11 | constexpr union constructors must initialize all data members |
initializes exactly one data member for non-empty unions |
| CWG 1366 | C++11 | classes with constexpr constructors whose function bodies are = default or = delete could have virtual base classes |
such classes can neither have virtual base classes |
| CWG 1595 | C++11 | constexpr delegating constructors required all involved constructors to be constexpr |
only requires the target constructor to be constexpr |
| CWG 1712 | C++14 | a constexpr variable template was required to have all its declarations contain the constexpr specifier[1] |
not required anymore |
| CWG 1911 | C++11 | constexpr constructors for non-literal types were not allowed | allowed in constant initialization |
| CWG 2004 | C++11 | copy/move of a union with a mutable member was allowed in a constant expression |
mutable variants disqualify implicit copy/move |
| CWG 2022 | C++98 | whether equivalent constexpr and non-constexpr function produce equal result might depend on whether copy elision is performed |
assume that copy elision is always performed in constant expressions |
| CWG 2163 | C++14 | labels were allowed in constexpr functions even though goto statements are prohibited |
labels also prohibited |
| CWG 2268 | C++11 | copy/move of a union with a mutable member was prohibited by the resolution of CWG issue 2004 |
allowed if the object is created within the constant expression |
| CWG 2278 | C++98 | the resolution of CWG issue 2022 was not implementable | assume that copy elision is never performed in constant expressions |
| CWG 2531 | C++11 | a non-inline variable became inline if it is redeclared with constexpr |
the variable does not become inline |
- ↑ Dies ist redundant, da es nicht mehr als eine Deklaration eines Variablentemplates mit dem constexpr-Spezifizierer geben kann.
[bearbeiten] Siehe auch
| Konstanter Ausdruck | definiert einen Ausdruck, der zur Kompilierzeit ausgewertet werden kann |
consteval-Spezifizierer(C++20) |
gibt an, dass eine Funktion eine unmittelbare Funktion ist, d. h., jeder Aufruf der Funktion muss in einer konstanten Auswertung erfolgen |
constinit-Spezifizierer(C++20) |
stellt sicher, dass eine Variable eine statische Initialisierung hat, d. h. Null-Initialisierung und konstante Initialisierung |
| C-Dokumentation für constexpr
| |