Differenza tra `constexpr` e` const`


593

Qual è la differenza tra constexpre const?

  • Quando posso usarne solo uno?
  • Quando posso usare entrambi e come dovrei sceglierne uno?

71
constexprcrea una costante di compilazione; constsignifica semplicemente che il valore non può essere modificato.
0x499602D2


Può essere che questo articolo della boost/hanabiblioteca possa chiarire alcuni constexprproblemi dove è possibile utilizzare constexpre dove non è possibile: boost.org/doc/libs/1_69_0/libs/hana/doc/html/…
Andry

@ 0x499602D2 " significa semplicemente che il valore non può essere modificato " Per uno scalare inizializzato con un valore letterale, un valore che non può essere modificato è anche una costante di tempo di compilazione.
curioso

@curiousguy Sì, il mio commento è stato molto semplificato. Devo ammettere che ero nuovo constexpranche allora :)
0x499602D2

Risposte:


587

Significato e sintassi di base

Entrambe le parole chiave possono essere utilizzate nella dichiarazione di oggetti e in funzioni. La differenza di base quando applicata agli oggetti è questa:

  • constdichiara un oggetto come costante . Ciò implica una garanzia che, una volta inizializzato, il valore di quell'oggetto non cambierà e il compilatore può sfruttare questo fatto per le ottimizzazioni. Aiuta anche a impedire al programmatore di scrivere codice che modifica oggetti che non avrebbero dovuto essere modificati dopo l'inizializzazione.

  • constexprdichiara un oggetto adatto all'uso in ciò che lo Standard chiama espressioni costanti . Ma nota che constexprnon è l'unico modo per farlo.

Quando applicata alle funzioni, la differenza di base è questa:

  • constpuò essere utilizzato solo per le funzioni membro non statiche, non per le funzioni in generale. Garantisce che la funzione membro non modifica nessuno dei membri di dati non statici.

  • constexprpuò essere utilizzato con funzioni sia membro che non membro, nonché costruttori. Dichiara la funzione adatta all'uso in espressioni costanti . Il compilatore lo accetterà solo se la funzione soddisfa determinati criteri (7.1.5 / 3,4), soprattutto (†) :

    • Il corpo della funzione deve essere non virtuale ed estremamente semplice: a parte i typedef e le returnasserzioni statiche, è consentita una sola istruzione. Nel caso di un costruttore, sono consentiti solo un elenco di inizializzazione, errori di battitura e asserzione statica. ( = defaulte = deletesono ammessi anche, però.)
    • A partire da C ++ 14, le regole sono più rilassate, ciò che è consentito da allora all'interno di una funzione constexpr: asmdichiarazione, gotoun'istruzione, un'istruzione con un'etichetta diversa da casee default, try-block, la definizione di una variabile di non letterale tipo, definizione di una variabile di durata dell'archiviazione statica o thread, definizione di una variabile per la quale non viene eseguita l'inizializzazione.
    • Gli argomenti e il tipo restituito devono essere tipi letterali (cioè, in generale, tipi molto semplici, tipicamente scalari o aggregati)

Espressioni costanti

Come detto sopra, constexprdichiara sia gli oggetti che le funzioni come idonei all'uso in espressioni costanti. Un'espressione costante è più che semplicemente costante:

  • Può essere utilizzato in luoghi che richiedono una valutazione in fase di compilazione, ad esempio i parametri del modello e gli identificatori delle dimensioni dell'array:

    template<int N>
    class fixed_size_list
    { /*...*/ };
    
    fixed_size_list<X> mylist;  // X must be an integer constant expression
    
    int numbers[X];  // X must be an integer constant expression
  • Ma nota:

    • Dichiarare qualcosa in quanto constexprnon garantisce necessariamente che verrà valutata in fase di compilazione. Esso può essere utilizzato per tale, ma può essere utilizzato in altri luoghi che vengono valutate in fase di esecuzione, pure.

    • Un oggetto può essere idoneo all'uso in espressioni costanti senza essere dichiarato constexpr. Esempio:

      int main()
      {
        const int N = 3;
        int numbers[N] = {1, 2, 3};  // N is constant expression
      }

    Ciò è possibile perché N, essendo costante e inizializzato al momento della dichiarazione con un valore letterale, soddisfa i criteri per un'espressione costante, anche se non è dichiarato constexpr.

Quindi, quando devo effettivamente utilizzare constexpr?

  • Un oggetto come Nsopra può essere usato come espressione costante senza essere dichiarato constexpr. Questo vale per tutti gli oggetti che sono:

    • const
    • di tipo integrale o di enumerazione e
    • inizializzato al momento della dichiarazione con un'espressione che è essa stessa un'espressione costante

    [Ciò è dovuto al § 5.19 / 2: un'espressione costante non deve includere una sottoespressione che implichi "una modifica da valore in valore a meno che [...] un valore di tipo integrale o di enumerazione [...]" Grazie a Richard Smith per aver corretto il mio in precedenza affermavano che questo era vero per tutti i tipi letterali.]

  • Perché una funzione sia adatta all'uso in espressioni costanti, deve essere dichiarata esplicitamente constexpr; non è sufficiente per soddisfare semplicemente i criteri per le funzioni di espressione costante. Esempio:

    template<int N>
    class list
    { };
    
    constexpr int sqr1(int arg)
    { return arg * arg; }
    
    int sqr2(int arg)
    { return arg * arg; }
    
    int main()
    {
      const int X = 2;
      list<sqr1(X)> mylist1;  // OK: sqr1 is constexpr
      list<sqr2(X)> mylist2;  // wrong: sqr2 is not constexpr
    }

Quando posso / dovrei usare entrambi conste constexpr insieme?

A. Nelle dichiarazioni di oggetti. Questo non è mai necessario quando entrambe le parole chiave si riferiscono allo stesso oggetto da dichiarare. constexprimplica const.

constexpr const int N = 5;

equivale a

constexpr int N = 5;

Tuttavia, si noti che potrebbero esserci situazioni in cui le parole chiave fanno riferimento a parti diverse della dichiarazione:

static constexpr int N = 3;

int main()
{
  constexpr const int *NP = &N;
}

Qui, NPviene dichiarato come indirizzo espressione costante, ovvero un puntatore che è esso stesso un'espressione costante. (Ciò è possibile quando l'indirizzo viene generato applicando l'operatore address a un'espressione costante statica / globale.) Qui, entrambi constexpre constsono richiesti: constexprsi riferisce sempre all'espressione dichiarata (qui NP), mentre si constriferisce a int(dichiara un puntatore- a-const). La rimozione di constrenderebbe l'espressione illegale (perché (a) un puntatore a un oggetto non const non può essere un'espressione costante, e (b) &Nè in realtà un puntatore a costante).

B. Nelle dichiarazioni di funzioni membro. In C ++ 11, constexprimplica const, mentre in C ++ 14 e C ++ 17 non è così. Una funzione membro dichiarata in C ++ 11 come

constexpr void f();

deve essere dichiarato come

constexpr void f() const;

sotto C ++ 14 per essere ancora utilizzabile come constfunzione.


3
L'IMO "non necessariamente valutato in fase di compilazione" è meno utile che considerarli come "valutati in fase di compilazione". I vincoli su un'espressione costante indicano che sarebbe relativamente facile per un compilatore valutarlo. Un compilatore deve lamentarsi se tali vincoli non sono soddisfatti. Poiché non ci sono effetti collaterali, non si può mai dire la differenza se un compilatore lo ha "valutato" o meno.
aschepler

10
@aschepler Certo. Il mio punto principale è che se si chiama una constexprfunzione su un'espressione non costante, ad esempio una variabile ordinaria, questa è perfettamente legale e la funzione verrà utilizzata come qualsiasi altra funzione. Non verrà valutato al momento della compilazione (perché non può). Forse pensi che sia ovvio, ma se affermassi che una funzione dichiarata come constexprsarà sempre valutata in fase di compilazione, potrebbe essere interpretata nel modo sbagliato.
jogojapan,

5
Sì, stavo parlando di constexproggetti, non di funzioni. Mi piace pensare constexpragli oggetti come forzare la valutazione dei valori in fase di compilazione e constexpralle funzioni come consentire che la funzione venga valutata in fase di compilazione o in fase di esecuzione, come appropriato.
aschepler

2
Una correzione: 'const' è solo una restrizione che NON puoi cambiare il valore di una variabile; non promette che il valore non cambierà (cioè da qualcun altro). È una proprietà di scrittura, non una proprietà di lettura.
Jared Grubb,

3
Questa frase: garantisce che la funzione membro non modifica nessuno dei membri di dati non statici. manca un dettaglio importante. I membri contrassegnati come mutablepossono anche essere modificati dalle constfunzioni dei membri.
Onnipotente il

119

constsi applica alle variabili e impedisce che vengano modificate nel codice.

constexprdice al compilatore che questa espressione si traduce in un valore di costante di tempo di compilazione , quindi può essere usato in posti come lunghezze di array, assegnazione a constvariabili, ecc. Il link fornito da Oli ha molti esempi eccellenti.

Fondamentalmente sono 2 concetti diversi e possono (e dovrebbero) essere usati insieme.



64

Panoramica

  • constgarantisce che un programma non modifica il valore di un oggetto . Tuttavia, constnon garantisce a quale tipo di inizializzazione subisce l'oggetto.

    Prendere in considerazione:

    const int mx = numeric_limits<int>::max();  // OK: runtime initialization

    La funzione max()restituisce semplicemente un valore letterale. Tuttavia, poiché l'inizializzatore è una chiamata di funzione, mxviene sottoposto all'inizializzazione di runtime. Pertanto, non è possibile utilizzarlo come espressione costante :

    int arr[mx];  // error: “constant expression required”
  • constexprè una nuova parola chiave C ++ 11 che ti libera dalla necessità di creare macro e letterali hardcoded. Garantisce inoltre, a determinate condizioni, che gli oggetti subiscano l' inizializzazione statica . Controlla il tempo di valutazione di un'espressione. Applicando la valutazione in fase di compilazione della sua espressione , è constexprpossibile definire espressioni costanti vere che sono cruciali per applicazioni time-critical, programmazione di sistema, modelli e in generale, in qualsiasi codice che si basa su costanti di compilazione-tempo.

Funzioni di espressione costante

Una funzione di espressione costante è una funzione dichiarata constexpr. Il suo corpo deve essere non virtuale e consistere in una sola istruzione return, a parte i typedef e le asserzioni statiche. Gli argomenti e il valore restituito devono avere tipi letterali. Può essere utilizzato con argomenti di espressione non costante, ma quando ciò viene fatto il risultato non è un'espressione costante.

Una funzione di espressione costante ha lo scopo di sostituire le macro e i letterali hardcoded senza sacrificare le prestazioni o la sicurezza del tipo.

constexpr int max() { return INT_MAX; }           // OK
constexpr long long_max() { return 2147483647; }  // OK
constexpr bool get_val()
{
    bool res = false;
    return res;
}  // error: body is not just a return statement

constexpr int square(int x)
{ return x * x; }  // OK: compile-time evaluation only if x is a constant expression
const int res = square(5);  // OK: compile-time evaluation of square(5)
int y = getval();
int n = square(y);          // OK: runtime evaluation of square(y)

Oggetti a espressione costante

Un oggetto a espressione costante è un oggetto dichiarato constexpr. Deve essere inizializzato con un'espressione costante o un valore costruito da un costruttore di espressioni costanti con argomenti di espressioni costanti.

Un oggetto a espressione costante si comporta come se fosse stato dichiarato const, tranne per il fatto che richiede l'inizializzazione prima dell'uso e il suo inizializzatore deve essere un'espressione costante. Di conseguenza, un oggetto di espressione costante può sempre essere usato come parte di un'altra espressione costante.

struct S
{
    constexpr int two();      // constant-expression function
private:
    static constexpr int sz;  // constant-expression object
};
constexpr int S::sz = 256;
enum DataPacket
{
    Small = S::two(),  // error: S::two() called before it was defined
    Big = 1024
};
constexpr int S::two() { return sz*2; }
constexpr S s;
int arr[s.two()];  // OK: s.two() called after its definition

Costruttori a espressione costante

Un costruttore a espressione costante è un costruttore dichiarato constexpr. Può avere un elenco di inizializzazione membro ma il suo corpo deve essere vuoto, a parte i typedef e le asserzioni statiche. I suoi argomenti devono avere tipi letterali.

Un costruttore di espressioni costanti consente al compilatore di inizializzare l'oggetto in fase di compilazione, a condizione che gli argomenti del costruttore siano tutte espressioni costanti.

struct complex
{
    // constant-expression constructor
    constexpr complex(double r, double i) : re(r), im(i) { }  // OK: empty body
    // constant-expression functions
    constexpr double real() { return re; }
    constexpr double imag() { return im; }
private:
    double re;
    double im;
};
constexpr complex COMP(0.0, 1.0);         // creates a literal complex
double x = 1.0;
constexpr complex cx1(x, 0);              // error: x is not a constant expression
const complex cx2(x, 1);                  // OK: runtime initialization
constexpr double xx = COMP.real();        // OK: compile-time initialization
constexpr double imaglval = COMP.imag();  // OK: compile-time initialization
complex cx3(2, 4.6);                      // OK: runtime initialization

Consigli dal libro Effective Modern C ++ di Scott Meyers su constexpr:

  • constexpr gli oggetti sono const e vengono inizializzati con valori noti durante la compilazione;
  • constexpr le funzioni producono risultati in fase di compilazione quando vengono chiamate con argomenti i cui valori sono noti durante la compilazione;
  • constexproggetti e funzioni possono essere utilizzati in una più ampia gamma di contesti rispetto a non constexproggetti e funzioni;
  • constexpr fa parte dell'interfaccia di un oggetto o di una funzione.

Fonte: utilizzo di constexpr per migliorare sicurezza, prestazioni e incapsulamento in C ++ .


Grazie per l'ottimo codice di esempio che mostra le diverse situazioni. Per quanto alcune delle altre spiegazioni siano, ho trovato molto più utile e comprensibile vedere il codice in azione. Mi ha davvero aiutato a consolidare la mia comprensione di ciò che sta succedendo.
RTHarston,

35

Secondo il libro "The C ++ Programming Language 4th Editon" di Bjarne Stroustrup
const : significa approssimativamente '' Prometto di non cambiare questo valore '' (§7.5). Viene utilizzato principalmente per specificare le interfacce, in modo che i dati possano essere passati a funzioni senza timore che vengano modificati.
Il compilatore applica la promessa fatta da const.
constexpr : significa approssimativamente '' da valutare in fase di compilazione '' (§10.4). Viene utilizzato principalmente per specificare le costanti, per consentire ad
esempio:

const int dmv = 17; // dmv is a named constant
int var = 17; // var is not a constant
constexpr double max1 = 1.4*square(dmv); // OK if square(17) is a constant expression
constexpr double max2 = 1.4square(var); // error : var is not a constant expression
const double max3 = 1.4square(var); //OK, may be evaluated at run time
double sum(const vector<double>&); // sum will not modify its argument (§2.2.5)
vector<double> v {1.2, 3.4, 4.5}; // v is not a constant
const double s1 = sum(v); // OK: evaluated at run time
constexpr double s2 = sum(v); // error : sum(v) not constant expression

Affinché una funzione sia utilizzabile in un'espressione costante, ovvero in un'espressione che verrà valutata dal compilatore, deve essere definita constexpr .
Per esempio:

constexpr double square(double x) { return xx; }


Per essere constexpr, una funzione deve essere piuttosto semplice: solo un'istruzione return che calcola un valore. Una funzione constexpr può essere utilizzata per argomenti non costanti, ma quando ciò viene fatto il risultato non è un'espressione costante. Permettiamo che una funzione constexpr sia chiamata con argomenti di espressione non costante in contesti che non richiedono espressioni costanti, quindi non dobbiamo definire essenzialmente la stessa funzione due volte: una volta per espressioni costanti e una volta per variabili.
In alcuni punti, sono richieste espressioni costanti dalle regole del linguaggio (ad es. Limiti di array (§2.2.5, §7.3), etichette dei casi (§2.2.4, §9.4.2), alcuni argomenti del modello (§25.2) e costanti dichiarate usando constexpr). In altri casi, la valutazione in fase di compilazione è importante per le prestazioni. Indipendentemente da problemi di prestazioni, la nozione di immutabilità (di un oggetto con uno stato immutabile) è una preoccupazione di progettazione importante (§10.4).


ci sono ancora problemi di prestazioni. Sembra che la funzione constexpr se valutata in fase di esecuzione potrebbe essere più lenta della versione della funzione non constexpr. Anche se abbiamo un valore costante dovremmo preferire "const" o "constexpr"? (più un assembly generato da una domanda di stile sembra lo stesso)
CoffeDeveloper l'

31

Entrambi conste constexprpossono essere applicati a variabili e funzioni. Anche se sono simili tra loro, in realtà sono concetti molto diversi.

Entrambi conste constexprsignificano che i loro valori non possono essere modificati dopo la loro inizializzazione. Quindi per esempio:

const int x1=10;
constexpr int x2=10;

x1=20; // ERROR. Variable 'x1' can't be changed.
x2=20; // ERROR. Variable 'x2' can't be changed.

La differenza principale tra conste constexprè il momento in cui i loro valori di inizializzazione sono noti (valutati). Mentre i valori delle constvariabili possono essere valutati sia in fase di compilazione che in fase di esecuzione, constexprvengono sempre valutati in fase di compilazione. Per esempio:

int temp=rand(); // temp is generated by the the random generator at runtime.

const int x1=10; // OK - known at compile time.
const int x2=temp; // OK - known only at runtime.
constexpr int x3=10; // OK - known at compile time.
constexpr int x4=temp; // ERROR. Compiler can't figure out the value of 'temp' variable at compile time so `constexpr` can't be applied here.

Il vantaggio principale di sapere se il valore è noto in fase di compilazione o in fase di esecuzione è il fatto che le costanti di tempo di compilazione possono essere utilizzate ogni volta che sono necessarie costanti di tempo di compilazione. Ad esempio, C ++ non consente di specificare array C con lunghezze variabili.

int temp=rand(); // temp is generated by the the random generator at runtime.

int array1[10]; // OK.
int array2[temp]; // ERROR.

Quindi significa che:

const int size1=10; // OK - value known at compile time.
const int size2=temp; // OK - value known only at runtime.
constexpr int size3=10; // OK - value known at compile time.


int array3[size1]; // OK - size is known at compile time.
int array4[size2]; // ERROR - size is known only at runtime time.
int array5[size3]; // OK - size is known at compile time.

Quindi le constvariabili possono definire sia costanti di tempo di compilazione del genere size1che possono essere utilizzate per specificare dimensioni di array che costanti di runtime come size2quelle conosciute solo in fase di runtime e non possono essere utilizzate per definire dimensioni di array. D'altra parte, constexprdefinire sempre costanti di tempo di compilazione in grado di specificare le dimensioni dell'array.

Entrambi conste constexprpossono essere applicati anche alle funzioni. Una constfunzione deve essere una funzione membro (metodo, operatore) in cui l'applicazione della constparola chiave significa che il metodo non può modificare i valori dei loro campi membro (non statici). Per esempio.

class test
{
   int x;

   void function1()
   {
      x=100; // OK.
   }

   void function2() const
   {
      x=100; // ERROR. The const methods can't change the values of object fields.
   }
};

A constexprè un concetto diverso. Contrassegna una funzione (membro o non membro) come funzione che può essere valutata in fase di compilazione se le costanti del tempo di compilazione vengono passate come argomenti . Ad esempio puoi scrivere questo.

constexpr int func_constexpr(int X, int Y)
{
    return(X*Y);
}

int func(int X, int Y)
{
    return(X*Y);
}

int array1[func_constexpr(10,20)]; // OK - func_constexpr() can be evaluated at compile time.
int array2[func(10,20)]; // ERROR - func() is not a constexpr function.

int array3[func_constexpr(10,rand())]; // ERROR - even though func_constexpr() is the 'constexpr' function, the expression 'constexpr(10,rand())' can't be evaluated at compile time.

A proposito, le constexprfunzioni sono le normali funzioni C ++ che possono essere chiamate anche se vengono passati argomenti non costanti. Ma in quel caso stai ottenendo i valori non constexpr.

int value1=func_constexpr(10,rand()); // OK. value1 is non-constexpr value that is evaluated in runtime.
constexpr int value2=func_constexpr(10,rand()); // ERROR. value2 is constexpr and the expression func_constexpr(10,rand()) can't be evaluated at compile time.

La constexprpuò essere applicato anche alle funzioni membro (metodi), operatori e persino costruttori. Per esempio.

class test2
{
    static constexpr int function(int value)
    {
        return(value+1);
    }

    void f()
    {
        int x[function(10)];


    }
};

Un campione più "pazzo".

class test3
{
    public:

    int value;

    // constexpr const method - can't chanage the values of object fields and can be evaluated at compile time.
    constexpr int getvalue() const
    {
        return(value);
    }

    constexpr test3(int Value)
        : value(Value)
    {
    }
};


constexpr test3 x(100); // OK. Constructor is constexpr.

int array[x.getvalue()]; // OK. x.getvalue() is constexpr and can be evaluated at compile time.

Inoltre, in C, constexpr intesiste ma è scrittoconst int
curioso

8

Come già sottolineato da @ 0x499602d2, constsi assicura che un valore non possa essere modificato solo dopo l'inizializzazione dove as constexpr(introdotto in C ++ 11) garantisce che la variabile sia una costante di tempo di compilazione.
Considera il seguente esempio (da LearnCpp.com):

cout << "Enter your age: ";
int age;
cin >> age;

const int myAge{age};        // works
constexpr int someAge{age};  // error: age can only be resolved at runtime

5

A const int varpuò essere impostato dinamicamente su un valore in fase di runtime e una volta impostato su quel valore, non può più essere modificato.

UN constexpr int var non può essere impostato in modo dinamico in fase di esecuzione, ma piuttosto in fase di compilazione. E una volta impostato su quel valore, non può più essere modificato.

Ecco un solido esempio:

int main(int argc, char*argv[]) {
    const int p = argc; 
    // p = 69; // cannot change p because it is a const
    // constexpr int q = argc; // cannot be, bcoz argc cannot be computed at compile time 
    constexpr int r = 2^3; // this works!
    // r = 42; // same as const too, it cannot be changed
}

Lo snippet sopra si compila bene e ho commentato quelli che lo hanno causato.

Le nozioni chiave qui da prendere in considerazione, sono le nozioni di compile timee run time. Nuove innovazioni sono state introdotte in C ++ per il più possibile ** know **certe cose in fase di compilazione per migliorare le prestazioni in fase di esecuzione.


3

Non credo che nessuna delle risposte chiarisca esattamente quali effetti collaterali ha, o in effetti, di cosa si tratta.

constexpre constallo spazio dei nomi / ambito dei file sono identici quando inizializzati con un valore letterale o un'espressione; ma con una funzione, constpuò essere inizializzato da qualsiasi funzione, ma constexprinizializzato da un non-constexpr (una funzione che non è contrassegnata con constexpr o un'espressione non constexpr) genererà un errore del compilatore. Entrambi constexpre constsono implicitamente un collegamento interno per le variabili (in realtà, non sopravvivono per arrivare allo stadio di collegamento se si compila -O1 e più forte, e staticnon impone al compilatore di emettere un simbolo di collegamento interno (locale) per consto constexprquando a -O1 o più forte; l'unica volta che lo fa è se prendi l'indirizzo della variabile conste constexprsarà un simbolo interno se non espresso con externieextern constexpr/const int i = 3;deve essere usato). Su una funzione, .constexprrende la funzione in modo permanente mai raggiungere lo stadio di collegamento (indipendentemente da externo inlinenella definizione o -O0 o -Oveloce), mentre constnon lo fa mai e statice ha inlinesolo questo effetto su -O1 e sopra. Quando una const/ constexprvariabile viene inizializzata da una o , o se la variabile non è un /constexpr funzione, il carico è sempre ottimizzato con qualsiasi flag di ottimizzazione, ma non è mai ottimizzato se la funzione è solo staticinlineconstconstexpr

Compilazione standard (-O0)

#include<iostream>
constexpr int multiply (int x, int y)
{

  return x * y;
}

extern const int val = multiply(10,10);
int main () {
  std::cout << val;
} 

compila a

val:
        .long   100  //extra external definition supplied due to extern

main:
        push    rbp
        mov     rbp, rsp
        mov     esi, 100 //substituted in as an immediate
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        pop     rbp
        ret

__static_initialization_and_destruction_0(int, int):
        . 
        . 
        . 

però

#include<iostream>
const int multiply (int x, int y)
{

  return x * y;
}

const int val = multiply(10,10); //constexpr is an error
int main () {
  std::cout << val;
}

Compila per

multiply(int, int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], edi
        mov     DWORD PTR [rbp-8], esi
        mov     eax, DWORD PTR [rbp-4]
        imul    eax, DWORD PTR [rbp-8]
        pop     rbp
        ret

main:
        push    rbp
        mov     rbp, rsp
        mov     eax, DWORD PTR val[rip]
        mov     esi, eax
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        pop     rbp
        ret

__static_initialization_and_destruction_0(int, int):
        . 
        . 
        . 
        mov     esi, 10
        mov     edi, 10
        call    multiply(int, int)
        mov     DWORD PTR val[rip], eax

Questo lo dimostra chiaramente constexpr provoca l'inizializzazione della const/constexprvariabile dell'ambito del file al momento della compilazione e non produce alcun simbolo globale, mentre il mancato utilizzo provoca l'inizializzazione primamain runtime.

Compilare usando -Ofast

Anche -Ofast non ottimizza il carico! https://godbolt.org/z/r-mhif , quindi è necessario constexpr


constexprle funzioni possono anche essere richiamate dall'interno di altre constexprfunzioni per lo stesso risultato. constexprsu una funzione impedisce anche l'uso di tutto ciò che non può essere fatto in fase di compilazione nella funzione; per esempio, una chiamata <<all'operatore sustd::cout .

constexpra block scope si comporta allo stesso modo in quanto produce un errore se inizializzato da una funzione non constexpr; il valore viene anche sostituito immediatamente.

Alla fine, il suo scopo principale è come la funzione inline di C, ma è efficace solo quando la funzione viene utilizzata per inizializzare le variabili dell'ambito dei file (che le funzioni non possono fare su C, ma possono su C ++ perché consente l'inizializzazione dinamica di file- variabili dell'ambito), tranne per il fatto che la funzione non può esportare un simbolo globale / locale anche sul linker, anche usando extern/static, che potresti usare inlinesu C; Le funzioni di assegnazione delle variabili a scopo di blocco possono essere integrate semplicemente usando un'ottimizzazione -O1 senza constexprsu C e C ++.


Bel punto sul linker. Potrebbe essere considerato più sicuro in generale utilizzare constexpr in quanto comporta minori perdite di simboli?
Neil McGill,

1
@NeilMcGill non proprio perché inline e statico impediranno al compilatore di non emettere un simbolo locale per moltiplicarsi se si compila usando -O1 o più forte. Constexpr è l'unico che ottimizza il carico per val, ma a parte questo è identico a mettere statico o inline prima della funzione. Ho dimenticato anche qualcos'altro. Constexpr è l'unica parola chiave che non emette un simbolo per la funzione su -O0, static e inline do
Lewis Kelsey

1

Prima di tutto, entrambi sono qualificatori in c ++. Una variabile dichiarata const deve essere inizializzata e non può essere modificata in futuro. Quindi generalmente una variabile dichiarata come const avrà un valore anche prima della compilazione.

Ma per constexpr è un po 'diverso.

Per constexpr, puoi dare un'espressione che potrebbe essere valutata durante la compilazione del programma.

Ovviamente, la variabile dichiarata come constexper non può essere modificata in futuro proprio come const.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.