Namensräume
Varianten
Aktionen

constexpr-Spezifizierer (seit C++11)

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
 
 

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)

  • Sie hat eine konstante Zerstörung, was bedeutet, dass eine der folgenden Bedingungen erfüllt sein muss:
  • Sie ist weder vom Klassentyp noch ein (möglicherweise mehrdimensionales) Array davon.
  • Sie ist vom Klassentyp mit einem constexpr-Destruktor oder ein (möglicherweise mehrdimensionales) Array davon, und für einen hypothetischen Ausdruck e, dessen einziger Effekt die Zerstörung des Objekts ist, wäre e ein Kern-Konstanter Ausdruck, wenn die Lebensdauer des Objekts und seiner nicht-veränderlichen Unterobjekte (aber nicht seiner veränderlichen Unterobjekte) als innerhalb von e beginnend betrachtet würde.

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.
  • Es handelt sich nicht um eine virtuelle Funktion.
(bis C++20)
  • Ihr Rückgabetyp (falls vorhanden) ist ein Literaltyp.
  • Jeder ihrer Parametertypen ist ein Literaltyp.
(bis C++23)
(seit C++20)
(bis C++14)
  • Ihr Funktionskörper ist = default, = delete, oder eine zusammengesetzte Anweisung, die(bis C++20) Folgendes nicht einschließt:
  • goto-Anweisungen
  • Anweisungen mit Labels außer case und default
(bis C++20)
  • Definitionen von Variablen nicht-literaler Typen
  • Definitionen von Variablen mit statischer oder Thread- Speicherdauer
(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:

[bearbeiten] constexpr-Konstruktor

Zusätzlich zu den Anforderungen von constexpr-Funktionen muss ein Konstruktor alle folgenden Bedingungen erfüllen, um constexpr-geeignet zu sein:

  • Sein Funktionskörper ist = delete oder erfüllt die folgenden zusätzlichen Anforderungen:
  • Wenn die Klasse eine Union mit Variant-Mitgliedern ist, wird genau eines davon initialisiert.
  • Wenn die Klasse eine Union-ähnliche Klasse ist, aber keine Union, wird für jedes ihrer anonymen Union-Mitglieder mit Variant-Mitgliedern genau eines davon initialisiert.
  • Jedes nicht-variante, nicht-statische Datenmitglied und jeder Basisklassen-Unterobjekt wird initialisiert.
(bis C++20)
  • Wenn der Konstruktor ein delegierender Konstruktor ist, ist der Zielkonstruktor ein constexpr-Konstruktor.
  • Wenn der Konstruktor ein nicht-delegierender Konstruktor ist, ist jeder Konstruktor, der zur Initialisierung von nicht-statischen Datenmitgliedern und Basisklassen-Unterobjekten ausgewählt wird, ein constexpr-Konstruktor.
(bis C++23)

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:

  • Für jedes Unterobjekt vom Klassentyp oder (möglicherweise mehrdimensionales) Array davon muss dieser Klassentyp einen constexpr-Destruktor haben.
(bis C++23)
  • Die Klasse hat keine virtuelle Basisklasse.
(seit C++20)

[bearbeiten] Hinweise

Da der noexcept-Operator für einen konstanten Ausdruck immer true zurückgibt, kann er verwendet werden, um zu prüfen, ob eine bestimmte Aufrufung einer constexpr-Funktion den konstanten Ausdruckszweig nimmt.

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

constexpr

[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
  1. 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[bearbeiten]
constinit-Spezifizierer(C++20) stellt sicher, dass eine Variable eine statische Initialisierung hat, d. h. Null-Initialisierung und konstante Initialisierung[bearbeiten]
C-Dokumentation für constexpr