Einleitung: Der ewige Kampf der Konstanten in C++
\n\nWenn Sie jemals in C++ programmiert haben (oder gerade erst angefangen haben, es zu lernen), sind Sie sicherlich auf const gestoßen. Dieser magische Vorsatz verspricht, dass sich der Wert einer Variable nicht ändert. Aber dann kam C++11 und brachte constexpr mit sich. Und die Verwirrung begann.
Auf Stack Overflow hat die Frage "What's the difference between constexpr and const?" Hunderttausende von Aufrufen gesammelt. Programmierer aller Couleur verwechseln diese beiden Konzepte und verwenden sie synonym. Aber das ist ein großer Fehler!
\n\nIn diesem Tutorial werden wir ein für alle Mal klären, wie sich constexpr und const in C++ unterscheiden. Sie werden erfahren:
- \n
- Was eine Compile-Zeit-Konstante und eine Laufzeit-Konstante ist. \n
- Warum
constnicht immer etwas mit Leistung zu tun hat. \n - Wie
constexprC++ in eine moderne und sichere Sprache verwandelt. \n - Wann Sie welche dieser Konstruktionen verwenden sollten. \n
Los geht's! 🚀
\n\nKapitel 1. const: Ein Versprechen, keine Garantie
\n\nBeginnen wir mit den Grundlagen. const in C++ ist ein Typqualifizierer, der dem Compiler sagt: "Hey, dieser Wert kann nach der Initialisierung nicht mehr geändert werden." Klingt einfach, aber es gibt einen Haken.
Beispiel 1: const in Aktion
\n\n#include <iostream>\n\nint main() {\n const int x = 42; // Versprechen: x wird sich nicht ändern\n // x = 100; // Compiler-Fehler — super!\n \n std::cout << x; // Gibt 42 aus\n return 0;\n}\n\nAlles schön. Aber was, wenn der Wert einer const-Variable erst zur Laufzeit berechnet wird?
Beispiel 2: const, das zur Laufzeit berechnet wird
\n\n#include <iostream>\n\nint get_value() {\n return 42; // Funktion gibt einen Wert zurück\n}\n\nint main() {\n const int y = get_value(); // Immer noch const?\n // Aber der Wert von y ist erst zur Laufzeit bekannt!\n \n // Versuchen wir es in einem Array zu verwenden:\n // int arr[y]; // Fehler! y ist keine Compile-Zeit-Konstante!\n \n return 0;\n}\n\nDa haben wir den Haken! const schützt die Variable vor Änderungen, aber garantiert nicht, dass ihr Wert zum Zeitpunkt der Kompilierung bekannt ist. Wenn der Wert von einer Funktion stammt, wird er erst beim Start des Programms berechnet.
Wichtige Erkenntnis: const ist ein Vertrag über Unveränderlichkeit, nicht über Berechenbarkeit zur Compile-Zeit.
Kapitel 2. constexpr: Konstanten, die im Code leben
\n\nHier kommt constexpr ins Spiel. Dieser Spezifizierer wurde in C++11 eingeführt und hat den Umgang mit Konstanten grundlegend verändert. constexpr bedeutet: "Dieser Wert muss zur Compile-Zeit berechnet werden." Oder zumindest kann er berechnet werden.
Beispiel 3: constexpr-Variable
\n\n#include <iostream>\n\nint main() {\n constexpr int z = 42; // Garantie: Wert ist zur Compile-Zeit bekannt\n \n int arr[z]; // Funktioniert! z ist eine Compile-Zeit-Konstante\n \n std::cout << sizeof(arr) / sizeof(arr[0]); // Gibt 42 aus\n return 0;\n}\n\nDer Unterschied ist offensichtlich: constexpr ermöglicht die Verwendung der Variable dort, wo der Compiler den genauen Wert kennen muss – zum Beispiel für die Größe eines Arrays oder Template-Parameter.
Beispiel 4: constexpr-Funktionen
\n\nDas Interessanteste sind jedoch die Funktionen. constexpr kann auf Funktionen angewendet werden, und dann können sie zur Compile-Zeit berechnet werden, wenn alle Argumente ebenfalls Compile-Zeit-Konstanten sind.
#include <iostream>\n\nconstexpr int square(int x) {\n return x * x;\n}\n\nint main() {\n constexpr int result = square(5); // Wird zur Compile-Zeit berechnet!\n int arr[result]; // Funktioniert\n \n std::cout << result; // 25\n \n int runtime_value =