Storia della questione
C++ originariamente supportava solo macro (#define) e costanti (const). Tuttavia, per definire valori durante la compilazione, questo non era sufficiente. In C++11 è stata introdotta la parola chiave constexpr, che consente di calcolare valori già in fase di compilazione, e non solo durante l'esecuzione del programma.
Problema
Prima dell'arrivo di constexpr, molte attività dovevano essere risolte o con macro (un semplice sostituto testuale senza sicurezza dei tipi), o utilizzando const, che non garantiva sempre l'esecuzione dell'espressione durante la compilazione. Ciò complicava l'ottimizzazione del programma e portava a un comportamento meno prevedibile.
Soluzione
constexpr garantisce al compilatore che l'espressione dichiarata sarà sicuramente calcolata in fase di compilazione, se possibile. Questo viene utilizzato per dichiarare funzioni, variabili e persino costruttori e metodi di classi che possono essere calcolati in modo sicuro ed efficace durante la compilazione.
Esempio di codice:
constexpr int Square(int x) { return x * x; } constexpr int size = Square(5); // size viene calcolato in fase di compilazione const int arr[size] = {}; // può essere utilizzato come dimensione dell'array
Caratteristiche principali:
Si può utilizzare qualsiasi funzione come constexpr?
No. La funzione deve soddisfare una serie di vincoli: deve essere abbastanza semplice, contenere una sola riga di return (fino a C++14) o solo codice calcolabile in modo costante (da C++14 in poi).
constexpr int f(int x) { return x + 2; } // ok constexpr int g(int x) { int y = x + 2; return y; } // fino a C++14: non si compila! dopo — può
Possono tutte le variabili constexpr essere calcolate durante la compilazione?
No. Se durante l'inizializzazione viene utilizzato un valore non costante o non è possibile calcolare l'espressione in fase di compilazione, ci sarà un errore.
int val; // constexpr int x = f(val); // Errore: val non è stato inizializzato!
Qual è la differenza tra constexpr e const?
const garantisce solo l'impossibilità di modifica, ma non garantisce il calcolo in fase di compilazione. constexpr richiede di calcolare il valore in fase di compilazione (se possibile).
const int x = time(nullptr); // ok, ma calcolato durante l'esecuzione constexpr int y = 42; // ok, calcolato in fase di compilazione
Uno sviluppatore utilizza #define PI 3.14 per tutti i calcoli dell'area del cerchio.
Vantaggi:
Svantaggi:
Uno sviluppatore utilizza constexpr double PI = 3.141592653589793; e funzioni constexpr template per i calcoli.
Vantaggi:
Svantaggi: