C'è una differenza tra le seguenti definizioni?
const double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
In caso contrario, quale stile è preferito in C ++ 11?
C'è una differenza tra le seguenti definizioni?
const double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
In caso contrario, quale stile è preferito in C ++ 11?
Risposte:
Credo che ci sia una differenza. Rinominiamoli in modo da poterne parlare più facilmente:
const double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;
Entrambi PI1
e PI2
sono costanti, il che significa che non è possibile modificarli. Tuttavia, è solo PI2
una costante di compilazione. Esso deve essere inizializzato in fase di compilazione. PI1
può essere inizializzato in fase di compilazione o di runtime. Inoltre, solo PI2
può essere utilizzato in un contesto che richiede una costante di tempo di compilazione. Per esempio:
constexpr double PI3 = PI1; // error
ma:
constexpr double PI3 = PI2; // ok
e:
static_assert(PI1 == 3.141592653589793, ""); // error
ma:
static_assert(PI2 == 3.141592653589793, ""); // ok
Quanto a quale dovresti usare? Usa quello che soddisfa le tue esigenze. Vuoi assicurarti di avere una costante di tempo di compilazione che può essere utilizzata in contesti in cui è richiesta una costante di tempo di compilazione? Vuoi essere in grado di inizializzarlo con un calcolo eseguito in fase di esecuzione? Eccetera.
const int N = 10; char a[N];
funziona e i limiti dell'array devono essere costanti tempo di compilazione.
PI1
in una costante integrale in fase di compilazione per l'uso in un array, ma non per l'uso come parametro di modello integrale non di tipo. Quindi la convertibilità in fase di compilazione di PI1
un tipo integrale mi sembra un po 'sbagliata.
enum
inizializzatore sono le uniche due differenze notevoli tra const
e constexpr
(e nessuna delle due funziona double
comunque).
1 / PI1
e 1 / PI2
può dare risultati diversi. Non credo che questo tecnicismo sia altrettanto importante dei consigli in questa risposta.
constexpr double PI3 = PI1;
funziona correttamente per me. (MSVS2013 CTP). Che cosa sto facendo di sbagliato?
Nessuna differenza qui, ma importa quando hai un tipo che ha un costruttore.
struct S {
constexpr S(int);
};
const S s0(0);
constexpr S s1(1);
s0
è una costante, ma non promette di essere inizializzato al momento della compilazione. s1
è contrassegnato constexpr
, quindi è una costante e, poiché anche S
il costruttore è contrassegnato constexpr
, verrà inizializzato in fase di compilazione.
Principalmente questo è importante quando l'inizializzazione in fase di runtime richiederebbe molto tempo e si desidera trasferire tale lavoro sul compilatore, dove è anche dispendioso in termini di tempo, ma non rallenta i tempi di esecuzione del programma compilato
constexpr
avrebbe portato a una diagnosi qualora il calcolo in fase di compilazione dell'oggetto fosse impossibile. Ciò che è meno chiaro è se una funzione che si aspetta un parametro costante possa essere eseguita in fase di compilazione se il parametro fosse dichiarato come const
e non come constexpr
: cioè, sarebbe constexpr int foo(S)
eseguito in fase di compilazione se chiamo foo(s0)
?
foo(s0)
verrebbe eseguito in fase di compilazione, ma non si sa mai: un compilatore può eseguire tali ottimizzazioni. Certamente, né gcc 4.7.2 né clang 3.2 mi permettono di compilareconstexpr a = foo(s0);
constexpr indica un valore costante e noto durante la compilazione.
const indica un valore che è solo costante; non è obbligatorio sapere durante la compilazione.
int sz;
constexpr auto arraySize1 = sz; // error! sz's value unknown at compilation
std::array<int, sz> data1; // error! same problem
constexpr auto arraySize2 = 10; // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr
Si noti che const non offre la stessa garanzia di constexpr, poiché gli oggetti const non devono essere inizializzati con valori noti durante la compilazione.
int sz;
const auto arraySize = sz; // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value unknown at compilation
Tutti gli oggetti constexpr sono const, ma non tutti gli oggetti const sono constexpr.
Se si desidera che i compilatori garantiscano che una variabile ha un valore che può essere utilizzato in contesti che richiedono costanti di compilazione, lo strumento da raggiungere è constexpr, non const.
A una costante simbolica constexpr deve essere assegnato un valore noto al momento della compilazione. Per esempio:
constexpr int max = 100;
void use(int n)
{
constexpr int c1 = max+7; // OK: c1 is 107
constexpr int c2 = n+7; // Error: we don’t know the value of c2
// ...
}
Per gestire i casi in cui il valore di una "variabile" inizializzata con un valore che non è noto al momento della compilazione ma che non cambia mai dopo l'inizializzazione, C ++ offre una seconda forma di costante (una const ). Per esempio:
constexpr int max = 100;
void use(int n)
{
constexpr int c1 = max+7; // OK: c1 is 107
const int c2 = n+7; // OK, but don’t try to change the value of c2
// ...
c2 = 7; // error: c2 is a const
}
Tali " variabili const " sono molto comuni per due motivi:
Riferimento: "Programmazione: principi e pratica usando il C ++" di Stroustrup