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.
Aufgabe
EinfachSie werden Ihren Code auf drei Dateien verteilen:
MathUtils.h: Definieren Sie Ihre mathematischen Funktionen für die Kompilierzeit.Erstellen Sie die folgenden Funktionen:
cube— eineconstexpr-Funktion, die einintentgegennimmt und dessen Kubikzahl (n * n * n) zurückgibt.triangularNumber— eineconstexpr-Funktion, die die n-te Dreieckszahl mit der Formel n * (n + 1) / 2 berechnet. Diese muss immer zur Kompilierzeit ausgewertet werden.sumOfSquares— eineconstexpr-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 einemconstexpr-Konstruktor, der drei Ganzzahlen entgegennimmt:width,heightunddepth. Speichern Sie diese als öffentliche Member. Fügen Sie außerdem eineconstexpr-Methode namensvolume()hinzu, die width * height * depth zurückgibt.Erstellen Sie unterhalb des Structs eine globale
constexpr-Konstante namensDEFAULT_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]
- Speichern Sie
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: 25Mit den Eingaben 5 und 12:
Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 5: 125
Sum of squares: 169Spickzettel
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 ausgewertetEine 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 KonstanteSie 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 erstelltProbier 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;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container12Moderne C++ Features
Move-Semantik & RvaluesPerfect ForwardingLambda-Ausdrücke im Detailstd::function & std::bindconstexpr und constevalStructured Bindingsoptional, variant, any