Menu
Coddy logo textTech

constexpr und consteval

Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 87 von 104.

C++ ermöglicht es, Berechnungen zur Kompilierzeit statt zur Laufzeit durchzuführen, was die Performance erheblich verbessern kann. Das Schlüsselwort constexpr (C++11) und das Schlüsselwort consteval (C++20) geben Ihnen die Kontrolle darüber, wann Ausdrücke ausgewertet werden.

Eine constexpr-Funktion kann zur Kompilierzeit ausgewertet werden, wenn ihr konstante Argumente übergeben werden, aber sie kann auch zur Laufzeit mit nicht-konstanten Eingaben ausgeführt werden:

#include <iostream>

constexpr int square(int n) {
    return n * n;
}

int main() {
    constexpr int compileTime = square(5);  // Wird zur Kompilierzeit ausgewertet
    
    int x = 7;
    int runtime = square(x);  // Wird zur Laufzeit ausgewertet
    
    std::cout << compileTime << "\n";  // 25
    std::cout << runtime << "\n";      // 49
}

Wenn Sie eine Auswertung zur Kompilierzeit garantieren müssen, verwenden Sie consteval. Eine consteval-Funktion muss eine Konstante erzeugen – sie mit Laufzeitwerten aufzurufen, führt zu einem Kompilierfehler:

consteval int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

int main() {
    constexpr int result = factorial(5);  // OK: 120 zur Kompilierzeit berechnet
    
    // int x = 5;
    // int bad = factorial(x);  // FEHLER: x ist keine Konstante
}

Sie können constexpr auch mit Variablen und sogar Klassenkonstruktoren verwenden, was es ermöglicht, ganze Objekte zur Kompilierzeit zu erstellen. Dies ist besonders nützlich für Nachschlagetabellen, Konfigurationswerte oder alle Daten, die sich während der Programmausführung nicht ändern.

challenge icon

Aufgabe

Einfach

Sie werden Ihren Code auf drei Dateien verteilen:

  • MathUtils.h: Definieren Sie Ihre mathematischen Funktionen für die Kompilierzeit.

    Erstellen Sie die folgenden Funktionen:

    • cube — eine constexpr-Funktion, die ein int entgegennimmt und dessen Kubikzahl (n * n * n) zurückgibt.
    • triangularNumber — eine constexpr-Funktion, die die n-te Dreieckszahl mit der Formel n * (n + 1) / 2 berechnet. Diese muss immer zur Kompilierzeit ausgewertet werden.
    • sumOfSquares — eine constexpr-Funktion, die zwei Ganzzahlen entgegennimmt und die Summe ihrer Quadrate (a*a + b*b) zurückgibt.
  • Config.h: Erstellen Sie eine Konfigurationsstruktur unter Verwendung von Kompilierzeit-Konstanten.

    Definieren Sie ein Config-Struct mit einem constexpr-Konstruktor, der drei Ganzzahlen entgegennimmt: width, height und depth. Speichern Sie diese als öffentliche Member. Fügen Sie außerdem eine constexpr-Methode namens volume() hinzu, die width * height * depth zurückgibt.

    Erstellen Sie unterhalb des Structs eine globale constexpr-Konstante namens DEFAULT_CONFIG, die mit den Werten 10, 20 und 5 initialisiert wird.

  • main.cpp: Lesen Sie zwei Ganzzahlen von der Eingabe ein, die Laufzeitwerte darstellen.

    Demonstrieren Sie zunächst die Auswertung zur Kompilierzeit, indem Sie constexpr-Variablen erstellen:

    • Speichern Sie cube(4) in einer constexpr-Variablen und geben Sie aus: Cube of 4: [value]
    • Speichern Sie triangularNumber(10) in einer constexpr-Variablen und geben Sie aus: 10th triangular number: [value]
    • Geben Sie das Volumen der Standardkonfiguration aus: Default volume: [value]

    Demonstrieren Sie dann, dass constexpr-Funktionen auch zur Laufzeit funktionieren können, indem Sie Ihre beiden Eingabewerte verwenden:

    • Rufen Sie cube() mit der ersten Eingabe auf und geben Sie aus: Cube of [input]: [result]
    • Rufen Sie sumOfSquares() mit beiden Eingaben auf und geben Sie aus: Sum of squares: [result]

Zum Beispiel mit den Eingaben 3 und 4:

Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 3: 27
Sum of squares: 25

Mit den Eingaben 5 und 12:

Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 5: 125
Sum of squares: 169

Spickzettel

C++ ermöglicht Berechnungen zur Kompilierzeit unter Verwendung der Schlüsselwörter constexpr (C++11) und consteval (C++20).

Eine constexpr-Funktion kann zur Kompilierzeit mit konstanten Argumenten ausgewertet werden, wird aber auch zur Laufzeit mit nicht-konstanten Eingaben ausgeführt:

constexpr int square(int n) {
    return n * n;
}

constexpr int compileTime = square(5);  // Zur Kompilierzeit ausgewertet

int x = 7;
int runtime = square(x);  // Zur Laufzeit ausgewertet

Eine consteval-Funktion muss zur Kompilierzeit ausgewertet werden. Der Aufruf mit Laufzeitwerten führt zu einem Kompilierfehler:

consteval int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

constexpr int result = factorial(5);  // OK: zur Kompilierzeit berechnet

// int x = 5;
// int bad = factorial(x);  // FEHLER: x ist keine Konstante

Sie können constexpr mit Variablen, Klassenkonstruktoren und Methoden verwenden, um die Objekterstellung zur Kompilierzeit zu ermöglichen:

struct Config {
    int width, height, depth;
    
    constexpr Config(int w, int h, int d) 
        : width(w), height(h), depth(d) {}
    
    constexpr int volume() const {
        return width * height * depth;
    }
};

constexpr Config config(10, 20, 5);  // Zur Kompilierzeit erstellt

Probier es selbst

#include <iostream>
#include "MathUtils.h"
#include "Config.h"

using namespace std;

int main() {
    // Lies zwei Ganzzahlen von der Eingabe
    int input1, input2;
    cin >> input1;
    cin >> input2;
    
    // TODO: Demonstriere die Auswertung zur Kompilierzeit
    // Erstelle eine constexpr Variable, die cube(4) speichert, und gib aus: "Cube of 4: [value]"
    
    // TODO: Erstelle eine constexpr Variable, die triangularNumber(10) speichert
    // und gib aus: "10th triangular number: [value]"
    
    // TODO: Gib die Lautstärke der Standardkonfiguration aus: "Default volume: [value]"
    
    // TODO: Demonstriere die Laufzeitnutzung von constexpr Funktionen
    // Rufe cube() mit input1 auf und gib aus: "Cube of [input1]: [result]"
    
    // TODO: Rufe sumOfSquares() mit beiden Eingaben auf
    // und gib aus: "Sum of squares: [result]"
    
    return 0;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung