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?
Je crois qu'il y a une différence. Renommons-les afin de pouvoir en parler plus facilement:
const double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;
PI1
et PI2
sont tous deux constants, ce qui signifie que vous ne pouvez pas les modifier. Cependant niquementPI2
est une constante de compilation. Il doit être initialisé à la compilation. PI1
peut être initialisé à la compilation ou à l'exécution. De plus, niquementPI2
peut être utilisé dans un contexte nécessitant une constante au moment de la 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
Pour ce que vous devriez utiliser? Utilisez ce qui répond à vos besoins. Voulez-vous vous assurer que vous avez 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.
Aucune différence ici, mais cela importe quand vous avez un type qui a un constructeur.
struct S {
constexpr S(int);
};
const S s0(0);
constexpr S s1(1);
s0
est une constante, mais elle ne promet pas d'être initialisée à la compilation. s1
est marqué constexpr
, il s'agit donc d'une constante et, étant donné que le constructeur de S
est également marqué constexpr
, il sera initialisé à la compilation.
Cela importe surtout lorsque l'initialisation au moment de l'exécution prend beaucoup de temps et que vous souhaitez appliquer ce travail au compilateur, qui prend également beaucoup de temps, mais ne ralentit pas le temps d'exécution du programme compilé.
constexpr indique une valeur constante et connue lors de la compilation.
const indique une valeur uniquement constante; il n'est pas obligatoire de 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 il n’est pas nécessaire d’initialiser les objets const 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 valeur a une valeur pouvant être utilisée dans des contextes nécessitant des constantes à la compilation, l'outil à atteindre est constexpr, pas const.
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" initialisée avec une valeur inconnue au moment de la compilation mais qui ne change jamais après l'initialisation, C++ propose une seconde forme de constante (a 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 "const variables" sont très courantes pour deux raisons:
Référence: "Programmation: principes et pratique utilisant le C++" de Stroustrup