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 Leistung erheblich verbessern kann. Das Schlüsselwort constexpr (C++11) und das Schlüsselwort consteval (C++20) geben dir Kontrolle darüber, wann Ausdrücke ausgewertet werden.
Eine constexpr-Funktion kann zur Kompilierzeit ausgewertet werden, wenn ihr konstante Argumente übergeben werden, sie kann aber 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); // Zur Kompilierzeit ausgewertet
int x = 7;
int runtime = square(x); // Zur Laufzeit ausgewertet
std::cout << compileTime << "\n"; // 25
std::cout << runtime << "\n"; // 49
}Wenn du die Auswertung zur Kompilierzeit garantieren musst, verwende consteval. Eine consteval-Funktion muss eine Konstante erzeugen – sie mit Laufzeitwerten aufzurufen, führt zu einem Kompilierungsfehler:
consteval int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
int main() {
constexpr int result = factorial(5); // OK: 120 zur Compile-Zeit berechnet
// int x = 5;
// int bad = factorial(x); // ERROR: x ist keine Konstante
}Du kannst constexpr auch mit Variablen und sogar Klassenkonstruktoren verwenden, wodurch ganze Objekte zur Kompilierzeit erstellt werden können. Dies ist besonders nützlich für Nachschlagetabellen, Konfigurationswerte oder Daten, die sich während der Programmausführung nicht ändern.
Aufgabe
EinfachDu organisierst deinen Code über drei Dateien:
MathUtils.h: Definiere deine Mathematikfunktionen zur Compile-Zeit.Erstelle die folgenden Funktionen:
cube: eineconstexpr-Funktion, die einintentgegennimmt und dessen Kubikzahl zurückgibt (n * n * n)triangularNumber: eineconstexpr-Funktion, die die n-te Dreieckszahl mithilfe der Formel n * (n + 1) / 2 berechnet. In dieser Aufgabe rufen wir sie ausschließlich in einem Compile-Zeit-Kontext auf und speichern ihr Ergebnis in einerconstexpr-Variablen.sumOfSquares: eineconstexpr-Funktion, die zwei Ganzzahlen entgegennimmt und die Summe ihrer Quadrate zurückgibt (a*a + b*b)
Config.h: Erstelle eine Konfigurationsstruktur mit Konstanten zur Compile-Zeit.Definiere eine
Config-Struktur mit einemconstexpr-Konstruktor, der drei Ganzzahlen entgegennimmt:width,heightunddepth. Speichere diese als öffentliche Member. Füge außerdem eineconstexpr-Methode namensvolume()hinzu, die width * height * depth zurückgibt.Erstelle unterhalb der Struktur eine globale
constexpr-Konstante namensDEFAULT_CONFIG, die mit den Werten 10, 20 und 5 initialisiert wird.main.cpp: Lies zwei Ganzzahlen aus der Eingabe ein, die Laufzeitwerte darstellen.Zeige zunächst die Auswertung zur Compile-Zeit, indem du
constexpr-Variablen erstellst:- Speichere
cube(4)in einer constexpr-Variablen und gib Folgendes aus:Cube of 4: [value] - Speichere
triangularNumber(10)in einer constexpr-Variablen und gib Folgendes aus:10th triangular number: [value] - Gib das Volumen der Standardkonfiguration aus:
Default volume: [value]
Zeige anschließend, dass
constexpr-Funktionen auch zur Laufzeit funktionieren können, indem du deine beiden Eingabewerte verwendest:- Rufe
cube()mit der ersten Eingabe auf und gib Folgendes aus:Cube of [input]: [result] - Rufe
sumOfSquares()mit beiden Eingaben auf und gib Folgendes aus:Sum of squares: [result]
- Speichere
Zum Beispiel bei den Eingaben 3 und 4:
Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 3: 27
Sum of squares: 25Bei den Eingaben 5 und 12:
Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 5: 125
Sum of squares: 169Probier es selbst
#include <iostream>
#include "MathUtils.h"
#include "Config.h"
using namespace std;
int main() {
// Lese 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: Create a constexpr variable storing triangularNumber(10)
// and print: "10th triangular number: [value]"
// TODO: Print the default config's volume: "Default volume: [value]"
// TODO: Demonstriere die Laufzeitverwendung von constexpr-Funktionen
// Rufe cube() mit input1 auf und gib aus: "Cube of [input1]: [result]"
// TODO: Call sumOfSquares() with both inputs
// and print: "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 & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer Container12Moderne C++-Funktionen
Move-Semantik & RvaluesPerfektes ForwardingLambda-Ausdrücke im Detailstd::function & std::bindconstexpr und constevalStrukturierte Bindungenoptional, variant, anyÜbe selbstständig: Online-C++-Compiler