const vs constexpr sur les variables


303

Y a-t-il une différence entre les définitions suivantes?

const     double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;

Sinon, quel style est préféré en C ++ 11?



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:

const     double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;

Les deux PI1et PI2sont constants, ce qui signifie que vous ne pouvez pas les modifier. Cependant, seule PI2 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, seul PI2 peut être utilisé dans un contexte qui nécessite une constante de temps de compilation. Par exemple:

constexpr double PI3 = PI1;  // error

mais:

constexpr double PI3 = PI2;  // ok

et:

static_assert(PI1 == 3.141592653589793, "");  // error

mais:

static_assert(PI2 == 3.141592653589793, "");  // ok

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.


60
Ê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é


3
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;
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

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;
const auto 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.


2
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.
Mayukh Sarkar

1
@MayukhSarkar Simplement Google C ++ pourquoi constexpr , par exemple stackoverflow.com/questions/4748083/…
underscore_d

18

Une constante symbolique constexpr doit recevoir une valeur connue au moment de la compilation. Par exemple:

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
    // ...
}

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:

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
}

Ces « variables const » sont très courantes pour deux raisons:

  1. C ++ 98 n'avait pas constexpr, donc les gens utilisaient const .
  2. 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


25
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
Aky
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.