Ricerca…
introduzione
constexpr
è una parola chiave che può essere usata per marcare il valore di una variabile come espressione costante, una funzione come potenzialmente utilizzabile nelle espressioni costanti, o (dal C ++ 17) un'istruzione if se ha solo uno dei suoi rami selezionati per essere compilato.
Osservazioni
La parola chiave constexpr
stata aggiunta in C ++ 11 ma per alcuni anni da quando è stato pubblicato lo standard C ++ 11, non tutti i principali compilatori lo hanno supportato. nel momento in cui è stato pubblicato lo standard C ++ 11. Al momento della pubblicazione di C ++ 14, tutti i principali compilatori supportano constexpr
.
variabili di constexpr
Una variabile dichiarata constexpr
è implicitamente const
e il suo valore può essere usato come espressione costante.
Confronto con #define
Un constexpr
è un sostituto sicuro per le espressioni di compilazione basate su #define
. Con constexpr
l'espressione valutata in fase di compilazione viene sostituita con il risultato. Per esempio:
int main()
{
constexpr int N = 10 + 2;
cout << N;
}
produrrà il seguente codice:
cout << 12;
Una macro in fase di compilazione basata sul pre-processore sarebbe diversa. Tenere conto:
#define N 10 + 2
int main()
{
cout << N;
}
produrrà:
cout << 10 + 2;
che sarà ovviamente convertito in cout << 10 + 2;
. Tuttavia, il compilatore dovrebbe fare più lavoro. Inoltre, crea un problema se non utilizzato correttamente.
Ad esempio (con #define
):
cout << N * 2;
le forme:
cout << 10 + 2 * 2; // 14
Ma un constexpr
pre-valutato constexpr
correttamente 24
.
Confronto con const
Una variabile const
è una variabile che ha bisogno di memoria per la sua memorizzazione. Un constexpr
non lo fa. Un constexpr
produce una costante di tempo di compilazione, che non può essere modificata. Si potrebbe obiettare che anche const
può essere cambiato. Ma considera:
int main()
{
const int size1 = 10;
const int size2 = abs(10);
int arr_one[size1];
int arr_two[size2];
}
Con la maggior parte dei compilatori la seconda istruzione fallirà (potrebbe funzionare con GCC, ad esempio). La dimensione di qualsiasi array, come forse saprai, deve essere un'espressione costante (cioè produce un valore in fase di compilazione). Alla seconda variabile size2
viene assegnato un valore deciso in fase di esecuzione (anche se si sa che è 10
, per il compilatore non è in fase di compilazione).
Ciò significa che una const
può o non può essere una vera costante in fase di compilazione. Non puoi garantire o far rispettare che un particolare valore const
sia assolutamente in fase di compilazione. Puoi usare #define
ma ha le sue insidie.
Quindi usa semplicemente:
int main()
{
constexpr int size = 10;
int arr[size];
}
constexpr
deve valutare un valore in fase di compilazione. Pertanto, non è possibile utilizzare:
constexpr int size = abs(10);
A meno che la funzione ( abs
) non stia restituendo un constexpr
.
Tutti i tipi di base possono essere inizializzati con constexpr
.
constexpr bool FailFatal = true;
constexpr float PI = 3.14f;
constexpr char* site= "StackOverflow";
È interessante notare che, e in modo conveniente, è possibile utilizzare anche l' auto
:
constexpr auto domain = ".COM"; // const char * const domain = ".COM"
constexpr auto PI = 3.14; // constexpr double
funzioni di constexpr
Una funzione dichiarata constexpr
è implicitamente in linea e le chiamate a tale funzione potenzialmente producono espressioni costanti. Ad esempio, la funzione seguente, se chiamata con argomenti con espressioni costanti, produce anche un'espressione costante:
constexpr int Sum(int a, int b)
{
return a + b;
}
Pertanto, il risultato della chiamata di funzione può essere utilizzato come un limite di matrice o un argomento di modello o per inizializzare una variabile di constexpr
:
int main()
{
constexpr int S = Sum(10,20);
int Array[S];
int Array2[Sum(20,30)]; // 50 array size, compile time
}
Si noti che se si rimuove constexpr
dalla specifica del tipo restituito dalla funzione, l'assegnazione a S
non funzionerà, poiché S
è una variabile constexpr
e deve essere assegnata a const di compilazione. Allo stesso modo, la dimensione dell'array non sarà anche un'espressione costante, se la funzione Sum
non è constexpr
.
La cosa interessante delle funzioni di constexpr
è che puoi anche usarlo come funzioni ordinarie:
int a = 20;
auto sum = Sum(a, abs(-20));
Sum
non sarà una funzione di constexpr
ora, sarà compilata come una funzione ordinaria, assumendo argomenti variabili (non costanti) e restituendo un valore non costante. Non è necessario scrivere due funzioni.
Significa anche che se si tenta di assegnare tale chiamata a una variabile non const, non verrà compilata:
int a = 20;
constexpr auto sum = Sum(a, abs(-20));
La ragione è semplice: a constexpr
deve essere assegnata una costante in tempo di compilazione. Tuttavia, la suddetta chiamata di funzione rende Sum
un non- constexpr
(il valore R è non-const, ma il valore-L si dichiara essere constexpr
).
La funzione constexpr
deve anche restituire una costante in fase di compilazione. Di seguito non verrà compilato:
constexpr int Sum(int a, int b)
{
int a1 = a; // ERROR
return a + b;
}
Perché a1
è una variabile non-constexpr e impedisce alla funzione di essere una vera funzione constexpr
. Rendere più constexpr
e assegnandogli a
anche non funziona - dal momento che il valore di a
(parametro in arrivo) non è ancora ancora noto:
constexpr int Sum(int a, int b)
{
constexpr int a1 = a; // ERROR
..
Inoltre, di seguito non verrà compilato anche:
constexpr int Sum(int a, int b)
{
return abs(a) + b; // or abs(a) + abs(b)
}
Poiché abs(a)
non è un'espressione costante (anche abs(10)
non funzionerà, dato che abs
non sta restituendo un constexpr int
!
Che dire di questo?
constexpr int Abs(int v)
{
return v >= 0 ? v : -v;
}
constexpr int Sum(int a, int b)
{
return Abs(a) + b;
}
Abbiamo creato la nostra funzione Abs
che è un constexpr
, e anche il corpo di Abs
non infrange nessuna regola. Inoltre, nel sito di chiamata (all'interno di Sum
), l'espressione constexpr
un constexpr
. Quindi, la chiamata a Sum(-10, 20)
sarà un'espressione costante in fase di compilazione risultante a 30
.
Statico se dichiarazione
L'istruzione if constexpr
può essere utilizzata per compilare il codice in modo condizionale. La condizione deve essere un'espressione costante. Il ramo non selezionato viene scartato. Un'istruzione scartata all'interno di un modello non viene istanziata. Per esempio:
template<class T, class ... Rest>
void g(T &&p, Rest &&...rs)
{
// ... handle p
if constexpr (sizeof...(rs) > 0)
g(rs...); // never instantiated with an empty argument list
}
Dichiarazioni Inoltre, variabili e funzioni che vengono ODR-utilizzati solo all'interno scartati non devono essere definiti, e scartati return
dichiarazioni non sono utilizzati per tipo di funzione di ritorno deduzione.
if constexpr
è diverso da #ifdef
. #ifdef
compila in modo condizionale il codice, ma solo in base a condizioni che possono essere valutate in fase di preelaborazione. Ad esempio, #ifdef
non può essere utilizzato per compilare in modo condizionale il codice in base al valore di un parametro del modello. D'altra parte, if constexpr
non può essere usato per scartare il codice sintatticamente non valido, mentre #ifdef
può #ifdef
.
if constexpr(false) {
foobar; // error; foobar has not been declared
std::vector<int> v("hello, world"); // error; no matching constructor
}