const vs constexpr sulle variabili


303

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?



Entrambi sono costanti in fase di compilazione. Ma puoi fare un const_cast del primo e scrivergli. Ma sarà ottimizzato da qualsiasi compilatore poiché questo non influenza le "letture" come accadono al momento della compilazione.
Bonita Montero,

Risposte:


347

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 PI1e PI2sono costanti, il che significa che non è possibile modificarli. Tuttavia, è solo PI2 una costante di compilazione. Esso deve essere inizializzato in fase di compilazione. PI1può 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.


60
Sei sicuro? Perché const int N = 10; char a[N];funziona e i limiti dell'array devono essere costanti tempo di compilazione.
fredoverflow,

10
Sono sicuro per quanto riguarda gli esempi che ho scritto (testati ciascuno di essi prima di pubblicare). Tuttavia il mio compilatore mi consente di convertire PI1in 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 PI1un tipo integrale mi sembra un po 'sbagliata.
Howard Hinnant,

34
@FredOverflow: gli indici di array non costanti hanno "funzionato" per circa un decennio (esiste ad esempio un'estensione g ++ per questo), ma ciò non significa che sia C ++ strettamente legale (anche se alcuni standard C o C ++ più recenti lo hanno reso legale , I dimenticato quale). Per quanto riguarda le differenze nelle costanti del compiletime, i parametri del modello e l'uso come enuminizializzatore sono le uniche due differenze notevoli tra conste constexpr(e nessuna delle due funziona doublecomunque).
Damon,

17
Il paragrafo 4 di 5.19 Espressioni costanti [expr.const] è anche una nota (non normativa) che sottolinea notoriamente che un'implementazione è autorizzata a eseguire l'aritmetica in virgola mobile in modo diverso (ad esempio per quanto riguarda l'accuratezza) in fase di compilazione che in fase di esecuzione. Quindi 1 / PI1e 1 / PI2può dare risultati diversi. Non credo che questo tecnicismo sia altrettanto importante dei consigli in questa risposta.
Luc Danton,

4
Ma constexpr double PI3 = PI1;funziona correttamente per me. (MSVS2013 CTP). Che cosa sto facendo di sbagliato?
NuPagadi

77

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 Sil 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


3
Sono d'accordo: la conclusione a cui sono arrivato è stata che constexpravrebbe 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 conste non come constexpr: cioè, sarebbe constexpr int foo(S)eseguito in fase di compilazione se chiamo foo(s0)?
Matthieu M.,

4
@MatthieuM: dubito che 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);
rici il

50

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.


2
Mi è piaciuta molto la tua spiegazione..puoi per favore commentare di più su Dove sono i casi che potremmo aver bisogno di usare costanti di tempo di compilazione in scenari di vita reale.
Mayukh Sarkar,

1
@MayukhSarkar Semplicemente Google C ++ perché constexpr , ad esempio stackoverflow.com/questions/4748083/...
underscore_d

18

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:

  1. C ++ 98 non aveva constexpr, quindi le persone usavano const .
  2. Elenca la voce "Variabili" che non sono espressioni costanti (il loro valore non è noto al momento della compilazione) ma che non modificano i valori dopo l'inizializzazione sono di per sé ampiamente utili.

Riferimento: "Programmazione: principi e pratica usando il C ++" di Stroustrup


25
Forse avresti dovuto menzionare che il testo nella tua risposta è preso alla lettera da "Programmazione: principi e pratica usando il C ++" di Stroustrup
Aky,
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.