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:

C ++ 11
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:

C ++ 11
int main()
{
    constexpr int size = 10;

    int arr[size];
}

constexpr deve valutare un valore in fase di compilazione. Pertanto, non è possibile utilizzare:

C ++ 11
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 .

C ++ 11
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 :

C ++ 11
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:

C ++ 11
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 :

C ++ 11
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:

C ++ 11
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:

C ++ 11
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:

C ++ 11
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:

C ++ 11
constexpr int Sum(int a, int b)
{
   constexpr int a1 = a;     // ERROR
   ..

Inoltre, di seguito non verrà compilato anche:

C ++ 11
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?

C ++ 11
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

C ++ 17

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
}


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow