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?
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?
Réponses:
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 PI1
et PI2
sont 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. PI1
peut ê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.
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 compilation PI1
en un type intégral me semble donc un peu hasardeuse.
enum
initialiseur sont les deux seules différences notables entre const
et constexpr
(et aucun ne fonctionne de double
toute façon).
1 / PI1
et 1 / 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.
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 au moment de la compilation. s1
est marqué constexpr
, c'est donc une constante et, comme S
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é
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é comme const
et non comme constexpr
: c'est-à-dire, serait constexpr int foo(S)
exécuté au moment de la compilation si j'appelle foo(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.
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.
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:
Référence: "Programmation: principes et pratique en utilisant C ++" par Stroustrup