Non credo che nessuna delle risposte chiarisca esattamente quali effetti collaterali ha, o in effetti, di cosa si tratta.
constexpr
e const
allo spazio dei nomi / ambito dei file sono identici quando inizializzati con un valore letterale o un'espressione; ma con una funzione, const
può essere inizializzato da qualsiasi funzione, ma constexpr
inizializzato da un non-constexpr (una funzione che non è contrassegnata con constexpr o un'espressione non constexpr) genererà un errore del compilatore. Entrambi constexpr
e const
sono 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 static
non impone al compilatore di emettere un simbolo di collegamento interno (locale) per const
o constexpr
quando a -O1 o più forte; l'unica volta che lo fa è se prendi l'indirizzo della variabile const
e constexpr
sarà un simbolo interno se non espresso con extern
ieextern constexpr/const int i = 3;
deve essere usato). Su una funzione, .constexpr
rende la funzione in modo permanente mai raggiungere lo stadio di collegamento (indipendentemente da extern
o inline
nella definizione o -O0 o -Oveloce), mentre const
non lo fa mai e static
e ha inline
solo questo effetto su -O1 e sopra. Quando una const
/ constexpr
variabile 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 static
inline
const
constexpr
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/constexpr
variabile 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
constexpr
le funzioni possono anche essere richiamate dall'interno di altre constexpr
funzioni per lo stesso risultato. constexpr
su 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
.
constexpr
a 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 inline
su C; Le funzioni di assegnazione delle variabili a scopo di blocco possono essere integrate semplicemente usando un'ottimizzazione -O1 senza constexpr
su C e C ++.
constexpr
crea una costante di compilazione;const
significa semplicemente che il valore non può essere modificato.