Achtergrond
C++ ondersteunde oorspronkelijk alleen macro's (#define) en constanten (const). Echter, voor het definiëren van waarden tijdens de compilatie was dit niet voldoende. In C++11 is het sleutelwoord constexpr geïntroduceerd, wat het mogelijk maakt om waarden al in de compileertijd te berekenen, en niet alleen tijdens de uitvoering van het programma.
Probleem
Voordat constexpr bestond, moesten veel problemen worden opgelost met macro's (een ruwe tekstuele vervanger zonder typeveiligheid) of met const, welke niet altijd garandeerde dat een expressie in de compileertijd werd uitgevoerd. Dit bemoeilijkte de optimalisatie van het programma en leidde tot minder voorspelbaar gedrag.
Oplossing
constexpr garandeert de compiler dat de gedeclareerde expressie noodzakelijkerwijs in de compileertijd berekend zal worden, indien mogelijk. Dit wordt gebruikt voor het declareren van functies, variabelen en zelfs constructeurs en methoden van klassen die veilig en efficiënt in de compileertijd berekend kunnen worden.
Voorbeeld code:
constexpr int Square(int x) { return x * x; } constexpr int size = Square(5); // size wordt berekend in de compileertijd const int arr[size] = {}; // kan gebruikt worden als grootte van de array
Belangrijke kenmerken:
Kan elke functie als constexpr worden gebruikt?
Nee. Een functie moet voldoen aan een aantal beperkingen: deze moet eenvoudig genoeg zijn, één return-statement bevatten (tot C++14) of enkel constant-berekenbare code omvatten (vanaf C++14 en hoger).
constexpr int f(int x) { return x + 2; } // ok constexpr int g(int x) { int y = x + 2; return y; } // tot C++14: compileert niet! daarna — mogelijk
Kunnen alle constexpr variabelen tijdens de compileertijd worden berekend?
Nee. Als bij de initiatie een niet-constant waarde wordt gebruikt of als een expressie niet berekend kan worden tijdens de compileertijd, zal er een fout optreden.
int val; // constexpr int x = f(val); // Fout: val is niet geïnitialiseerd!
Wat is het verschil tussen constexpr en const?
const garandeert alleen dat de waarde niet kan worden gewijzigd, maar garandeert niet dat deze in de compileertijd wordt berekend. constexpr vereist dat de waarde tijdens de compileertijd wordt berekend (indien mogelijk).
const int x = time(nullptr); // ok, maar wordt in de uitvoering berekend constexpr int y = 42; // ok, wordt in de compileertijd berekend
Een ontwikkelaar gebruikt #define PI 3.14 voor alle berekeningen van de oppervlakte van een cirkel.
Voordelen:
Nadelen:
Een ontwikkelaar gebruikt constexpr double PI = 3.141592653589793; en sjabloon constexpr-functies voor berekeningen.
Voordelen:
Nadelen: