Geschichte der Frage
C++ unterstützte ursprünglich nur Makros (#define) und Konstanten (const). Um Werte zur Compile-Zeit zu definieren, war dies jedoch nicht ausreichend. In C++11 wurde das Schlüsselwort constexpr eingeführt, das es ermöglicht, Werte bereits zur Compile-Zeit zu berechnen, nicht nur zur Laufzeit des Programms.
Problem
Vor der Einführung von constexpr mussten viele Aufgaben entweder mit Hilfe von Makros (ein grober Text-Ersatz ohne Typensicherheit) oder mit const gelöst werden, das nicht immer garantierte, dass der Ausdruck zur Compile-Zeit ausgeführt wird. Dies erschwerte die Optimierung des Programms und führte zu weniger vorhersehbarem Verhalten.
Lösung
constexpr garantiert dem Compiler, dass der deklarierte Ausdruck unbedingt zur Compile-Zeit berechnet wird, wenn dies möglich ist. Dies wird zur Deklaration von Funktionen, Variablen und sogar Konstruktoren und Methoden von Klassen verwendet, die sicher und effizient zur Compile-Zeit berechnet werden können.
Beispielcode:
constexpr int Square(int x) { return x * x; } constexpr int size = Square(5); // size wird zur Compile-Zeit berechnet const int arr[size] = {}; // kann als Array-Größe verwendet werden
Wichtige Eigenschaften:
Kann jede Funktion als constexpr verwendet werden?
Nein. Die Funktion muss eine Reihe von Einschränkungen erfüllen: Sie muss einfach genug sein, darf nur eine return-Anweisung enthalten (bis C++14) oder muss nur konstant berechenbaren Code enthalten (ab C++14 und höher).
constexpr int f(int x) { return x + 2; } // ok constexpr int g(int x) { int y = x + 2; return y; } // bis C++14: kompiliert nicht! danach — möglich
Können alle constexpr-Variablen zur Compile-Zeit berechnet werden?
Nein. Wenn bei der Initialisierung ein nicht-konstantes Wert verwendet wird oder der Ausdruck zur Compile-Zeit nicht berechnet werden kann, tritt ein Fehler auf.
int val; // constexpr int x = f(val); // Fehler: val ist nicht initialisiert!
Was ist der Unterschied zwischen constexpr und const?
const garantiert nur, dass keine Veränderung möglich ist, garantiert jedoch nicht die Berechnung zur Compile-Zeit. constexpr erfordert, dass der Wert zur Compile-Zeit berechnet wird (wenn möglich).
const int x = time(nullptr); // ok, wird zur Laufzeit berechnet constexpr int y = 42; // ok, wird zur Compile-Zeit berechnet
Ein Entwickler verwendet #define PI 3.14 für alle Berechnungen der Fläche eines Kreises.
Vorteile:
Nachteile:
Ein Entwickler verwendet constexpr double PI = 3.141592653589793; und templatisierte constexpr-Funktionen für Berechnungen.
Vorteile:
Nachteile: