Les deux sont constants au moment de la compilation. Mais vous pouvez faire une const_cast de la première et y écrire. Mais il sera optimisé par n'importe quel compilateur car cela n'influence pas les "lectures" comme elles se produisent au moment de la compilation.
Bonita Montero
Réponses:
347
Je pense qu'il y a une différence. Renommons-les pour pouvoir en parler plus facilement:
Les deux PI1et PI2sont constants, ce qui signifie que vous ne pouvez pas les modifier. Cependant, seulePI2 est une constante de temps de compilation. Il doit être initialisé au moment de la compilation. PI1peut être initialisé au moment de la compilation ou de l'exécution. De plus, seulPI2 peut être utilisé dans un contexte qui nécessite une constante de temps de compilation. Par exemple:
Quant à savoir que vous devez utiliser? Utilisez celui qui répond à vos besoins. Voulez-vous vous assurer que vous disposez d'une constante de temps de compilation qui peut être utilisée dans des contextes où une constante de temps de compilation est requise? Voulez-vous pouvoir l'initialiser avec un calcul effectué au moment de l'exécution? Etc.
Êtes-vous sûr? Parce que ça const int N = 10; char a[N];marche, et les limites du tableau doivent être des constantes au moment de la compilation.
fredoverflow
10
Je suis sûr en ce qui concerne les exemples que j'ai écrits (j'ai testé chacun d'eux avant de poster). Cependant, mon compilateur me permet de convertir PI1en une constante intégrale au moment de la compilation pour une utilisation dans un tableau, mais pas pour une utilisation en tant que paramètre de modèle intégral non type. La convertibilité au moment de la compilation PI1en un type intégral me semble donc un peu hasardeuse.
Howard Hinnant
34
@FredOverflow: les indices de tableau non const "ont fonctionné" pendant environ une décennie (il y a par exemple une extension g ++ pour cela), mais cela ne signifie pas qu'il s'agit d'un C ++ strictement légal (bien qu'un standard C ou C ++ plus récent le rende légal , je oublié lequel). En ce qui concerne les différences dans les constantes de temps de compilation, les paramètres de modèle et l'utilisation comme enuminitialiseur sont les deux seules différences notables entre constet constexpr(et aucun ne fonctionne de doubletoute façon).
Damon
17
Le paragraphe 4 de 5.19 Expressions constantes [expr.const] est également une note (non normative) qui souligne que l'implémentation est autorisée à faire l'arithmétique à virgule flottante différemment (par exemple en ce qui concerne la précision) au moment de la compilation qu'au moment de l'exécution. Donc, 1 / PI1et 1 / PI2peut donner des résultats différents. Je ne pense pas que cette technicité soit tout aussi importante que les conseils de cette réponse.
Luc Danton
4
Mais cela constexpr double PI3 = PI1;fonctionne correctement pour moi. (CTP MSVS2013). Qu'est-ce que je fais mal?
NuPagadi
77
Aucune différence ici, mais cela importe lorsque vous avez un type qui a un constructeur.
struct S {constexpr S(int);};const S s0(0);constexpr S s1(1);
s0est une constante, mais elle ne promet pas d'être initialisée au moment de la compilation. s1est marqué constexpr, c'est donc une constante et, comme Sle constructeur de est également marqué constexpr, il sera initialisé au moment de la compilation.
Généralement, cela importe lorsque l'initialisation à l'exécution prend du temps et que vous souhaitez pousser ce travail sur le compilateur, où il prend également du temps, mais ne ralentit pas le temps d'exécution du programme compilé
Je suis d'accord: la conclusion à laquelle je suis arrivé était que constexprcela conduirait à un diagnostic si le calcul de l'objet au moment de la compilation était impossible. Ce qui est moins clair est de savoir si une fonction qui attend un paramètre constant pourrait être exécutée au moment de la compilation si le paramètre était déclaré comme constet non comme constexpr: c'est-à-dire, serait constexpr int foo(S)exécuté au moment de la compilation si j'appelle foo(s0)?
Matthieu M.
4
@MatthieuM: Je doute que foo(s0)cela soit exécuté au moment de la compilation, mais on ne sait jamais: un compilateur est autorisé à faire de telles optimisations. Certes, ni gcc 4.7.2 ni clang 3.2 ne me permettent de compilerconstexpr a = foo(s0);
rici
50
constexpr indique une valeur constante et connue lors de la compilation. const indique une valeur qui n'est que constante; il n'est pas obligatoire de le savoir lors de la compilation.
int sz;constexprauto arraySize1 = sz;// error! sz's value unknown at compilation
std::array<int, sz> data1;// error! same problemconstexprauto arraySize2 =10;// fine, 10 is a compile-time constant
std::array<int, arraySize2> data2;// fine, arraySize2 is constexpr
Notez que const n'offre pas la même garantie que constexpr, car les objets const n'ont pas besoin d'être initialisés avec des valeurs connues lors de la compilation.
int sz;constauto arraySize = sz;// fine, arraySize is const copy of sz
std::array<int, arraySize> data;// error! arraySize's value unknown at compilation
Tous les objets constexpr sont const, mais tous les objets const ne sont pas constexpr.
Si vous voulez que les compilateurs garantissent qu'une variable a une valeur qui peut être utilisée dans des contextes nécessitant des constantes au moment de la compilation, l'outil à atteindre est constexpr, pas const.
J'ai beaucoup aimé votre explication ... pouvez-vous s'il vous plaît commenter plus sur Où sont les cas dont nous pourrions avoir besoin pour compiler des constantes de temps dans des scénarios réels.
Une constante symbolique constexpr doit recevoir une valeur connue au moment de la compilation. Par exemple:
constexprint max =100;void use(int n){constexprint c1 = max+7;// OK: c1 is 107constexprint c2 = n+7;// Error: we don’t know the value of c2// ...}
Pour gérer les cas où la valeur d'une «variable» qui est initialisée avec une valeur qui n'est pas connue au moment de la compilation mais qui ne change jamais après l'initialisation, C ++ propose une deuxième forme de constante (une const ). Par exemple:
constexprint max =100;void use(int n){constexprint c1 = max+7;// OK: c1 is 107constint c2 = n+7;// OK, but don’t try to change the value of c2// ...
c2 =7;// error: c2 is a const}
Ces « variables const » sont très courantes pour deux raisons:
C ++ 98 n'avait pas constexpr, donc les gens utilisaient const .
L'élément de liste «Variables» qui ne sont pas des expressions constantes (leur valeur n'est pas connue au moment de la compilation) mais qui ne changent pas de valeurs après l'initialisation est en soi très utile.
Référence: "Programmation: principes et pratique en utilisant C ++" par Stroustrup
Peut-être auriez-vous dû mentionner que le texte de votre réponse est extrait textuellement de "Programmation: principes et pratique en C ++" par Stroustrup
Réponses:
Je pense qu'il y a une différence. Renommons-les pour pouvoir en parler plus facilement:
Les deux
PI1
etPI2
sont constants, ce qui signifie que vous ne pouvez pas les modifier. Cependant, seulePI2
est une constante de temps de compilation. Il doit être initialisé au moment de la compilation.PI1
peut être initialisé au moment de la compilation ou de l'exécution. De plus, seulPI2
peut être utilisé dans un contexte qui nécessite une constante de temps de compilation. Par exemple:mais:
et:
mais:
Quant à savoir que vous devez utiliser? Utilisez celui qui répond à vos besoins. Voulez-vous vous assurer que vous disposez d'une constante de temps de compilation qui peut être utilisée dans des contextes où une constante de temps de compilation est requise? Voulez-vous pouvoir l'initialiser avec un calcul effectué au moment de l'exécution? Etc.
la source
const int N = 10; char a[N];
marche, et les limites du tableau doivent être des constantes au moment de la compilation.PI1
en une constante intégrale au moment de la compilation pour une utilisation dans un tableau, mais pas pour une utilisation en tant que paramètre de modèle intégral non type. La convertibilité au moment de la compilationPI1
en un type intégral me semble donc un peu hasardeuse.enum
initialiseur sont les deux seules différences notables entreconst
etconstexpr
(et aucun ne fonctionne dedouble
toute façon).1 / PI1
et1 / PI2
peut donner des résultats différents. Je ne pense pas que cette technicité soit tout aussi importante que les conseils de cette réponse.constexpr double PI3 = PI1;
fonctionne correctement pour moi. (CTP MSVS2013). Qu'est-ce que je fais mal?Aucune différence ici, mais cela importe lorsque vous avez un type qui a un constructeur.
s0
est une constante, mais elle ne promet pas d'être initialisée au moment de la compilation.s1
est marquéconstexpr
, c'est donc une constante et, commeS
le constructeur de est également marquéconstexpr
, il sera initialisé au moment de la compilation.Généralement, cela importe lorsque l'initialisation à l'exécution prend du temps et que vous souhaitez pousser ce travail sur le compilateur, où il prend également du temps, mais ne ralentit pas le temps d'exécution du programme compilé
la source
constexpr
cela conduirait à un diagnostic si le calcul de l'objet au moment de la compilation était impossible. Ce qui est moins clair est de savoir si une fonction qui attend un paramètre constant pourrait être exécutée au moment de la compilation si le paramètre était déclaré commeconst
et non commeconstexpr
: c'est-à-dire, seraitconstexpr int foo(S)
exécuté au moment de la compilation si j'appellefoo(s0)
?foo(s0)
cela soit exécuté au moment de la compilation, mais on ne sait jamais: un compilateur est autorisé à faire de telles optimisations. Certes, ni gcc 4.7.2 ni clang 3.2 ne me permettent de compilerconstexpr a = foo(s0);
constexpr indique une valeur constante et connue lors de la compilation.
const indique une valeur qui n'est que constante; il n'est pas obligatoire de le savoir lors de la compilation.
Notez que const n'offre pas la même garantie que constexpr, car les objets const n'ont pas besoin d'être initialisés avec des valeurs connues lors de la compilation.
Tous les objets constexpr sont const, mais tous les objets const ne sont pas constexpr.
Si vous voulez que les compilateurs garantissent qu'une variable a une valeur qui peut être utilisée dans des contextes nécessitant des constantes au moment de la compilation, l'outil à atteindre est constexpr, pas const.
la source
Une constante symbolique constexpr doit recevoir une valeur connue au moment de la compilation. Par exemple:
Pour gérer les cas où la valeur d'une «variable» qui est initialisée avec une valeur qui n'est pas connue au moment de la compilation mais qui ne change jamais après l'initialisation, C ++ propose une deuxième forme de constante (une const ). Par exemple:
Ces « variables const » sont très courantes pour deux raisons:
Référence: "Programmation: principes et pratique en utilisant C ++" par Stroustrup
la source